"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = __importDefault(require("express"));
const fetch_parcel_by_pin_1 = require("../../fetch/fetch-parcel-by-pin");
const fetch_from_mongo_1 = require("../../fetch/fetch-from-mongo");
const fetch_parcel_data_by_box_1 = require("../../fetch/fetch-parcel-data-by-box");
const fetch_parcel_by_polygon_1 = require("../../fetch/fetch-parcel-by-polygon");
const axios_1 = __importDefault(require("axios"));
const calculate_centroid_1 = require("../../utils/polyline/calculate-centroid");
const fetch_ngs_data_by_box_1 = require("../../fetch/fetch-ngs-data-by-box");
const database_1 = require("../../config/database");
const fetch_flood_by_post_1 = require("../../fetch/fetch-flood-by-post");
const fetch_zoning_by_post_1 = require("../../fetch/fetch-zoning-by-post");
const constants_1 = require("../../utils/constants/constants");
/**
* Express router for handling county-based parcel data requests.
*
* @type {express.Router}
*/
const router = express_1.default.Router();
/**
* Fetches parcel data for a given state, county, and PIN (Parcel Identification Number).
*
* If no PIN is provided, an example PIN from the county schema is used. Fetches the parcel
* data using the county schema's base URL and spatial reference.
*
* @name GET /:state/:county/pin
* @function
* @memberof router
* @async
* @param {Request} req - The Express request object.
* @param {string} req.params.state - The state code (e.g., "SC").
* @param {string} req.params.county - The county name (e.g., "Horry").
* @param {string} [req.query.pin] - The optional Parcel Identification Number (PIN) for fetching data.
* @param {Response} res - The Express response object.
* @returns {Promise<void>} JSON response containing parcel data or an error message.
* @example
* // Example request:
* // GET /SC/Horry/pin?pin=123456
*
* // Example response:
* // {
* // features: [
* // {
* // type: 'parcel',
* // layer: 'VB-PROP-BNDY-LINE-E',
* // attributes: { ... },
* // centroid: { x: 12345, y: 67890 },
* // geometry: { rings: [ ... ] }
* // }
* // ]
* // }
*/
router.get('/:state/:county/pin', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
const { state, county } = req.params;
let pin = req.query.pin;
const layer = 'VB-PROP-BNDY-LINE-E';
console.log(state, county);
try {
// Fetch the county schema from the database
const countySchema = yield (0, fetch_from_mongo_1.fetchCountySchema)(state, county);
if (!pin) {
// Use example PIN if none is provided
pin = countySchema.examplePin;
}
let pinString = `${pin}`;
// Check and format PIN type based on schema
const pinType = countySchema.esriPinType;
if (pinType === 'esriFieldTypeString') {
pinString = `'${pin}'`;
}
// Fetch parcel data using the schema's base URL and spatial reference
const parcelUrl = countySchema.baseUrls.parcel;
const spatialReference = countySchema.spatialReference;
const schema = countySchema.keys;
const results = yield (0, fetch_parcel_by_pin_1.fetchDataForPin)(county, pinString, parcelUrl, spatialReference, schema, layer);
res.json(results);
}
catch (error) {
console.error('Error sending response:', error);
res.status(500).send('An error occurred while fetching parcel data');
}
}));
router.get('/:state/:county/box/:type', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
let baseUrl = '';
const { state, county, type } = req.params;
const geometry = req.query.geometry;
if (!state || !county || !type) {
return res.status(400).json({ message: 'State, County, or Type parameter is missing.' });
}
if (!geometry) {
return res.status(400).json({ message: 'Geometry box parameter is missing.' });
}
const { xmin, ymin, xmax, ymax } = (0, fetch_parcel_data_by_box_1.parseGeometryBox)(geometry);
if (!xmin || !ymin || !xmax || !ymax) {
return res.status(400).send('Missing required query parameters: xmin, ymin, xmax, ymax');
}
try {
const countySchema = yield (0, fetch_from_mongo_1.fetchCountySchema)(state, county);
const spatialReference = countySchema.spatialReference;
const layersDb = yield (0, database_1.connectToDatabase)('available_layers');
const layerCollection = yield layersDb.collection('layers').findOne({}, { projection: { _id: 0 } });
const layer = layerCollection && layerCollection[type] ? layerCollection[type] : "0";
console.log(layer);
// put a schema file on db that will update if a new layer type is added. Ie. parcel, building, flood, etc.
if (type === 'parcel') {
baseUrl = countySchema.baseUrls.parcel;
}
else if (type === 'building') {
baseUrl = countySchema.baseUrls.building;
}
else if (type === 'flood') {
baseUrl = 'https://hazards.fema.gov/arcgis/rest/services/public/NFHL/MapServer/28/query';
}
else if (type === 'roads') {
baseUrl = countySchema.baseUrls.roads;
}
else if (type === 'zoning') {
baseUrl = countySchema.baseUrls.zoning;
}
else if (type === 'boundary') {
baseUrl = countySchema.baseUrls.boundary;
}
else if (type === 'noaa') {
baseUrl = 'https://services2.arcgis.com/C8EMgrsFcRFL6LrL/arcgis/rest/services/NGS_Datasheets_Feature_Service/FeatureServer/1/query';
}
else if (type === 'contour') {
baseUrl = countySchema.baseUrls.contour;
}
else if (type === 'zip') {
baseUrl = countySchema.baseUrls.zip;
}
const results = yield (0, fetch_parcel_data_by_box_1.fetchParcelDataByBbox)(Number(xmin), Number(ymin), Number(xmax), Number(ymax), baseUrl, spatialReference, county, countySchema.keys, type, layer);
res.json(results);
}
catch (error) {
console.error('Error fetching parcel data:', error);
res.status(500).send('An error occurred while fetching parcel data');
}
finally {
//await closeClient();
}
}));
router.get('/:state/:county/adjoiners', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
const { state, county } = req.params;
const pin = req.query.pin;
let pinString = `${pin}`;
console.log(pin);
if (!pin) {
return res.status(400).send('Missing required query parameter: pin');
}
try {
const countySchema = yield (0, fetch_from_mongo_1.fetchCountySchema)(state, county);
const layersDb = yield (0, database_1.connectToDatabase)('available_layers');
const layerCollection = yield layersDb.collection('layers').findOne({}, { projection: { _id: 0 } });
const layer = layerCollection && layerCollection['parcel'] ? layerCollection['parcel'] : "0";
console.log(layer);
const pinType = countySchema.esriPinType;
if (pinType === 'esriFieldTypeString') {
pinString = `'${pin}'`;
console.log('pinType is string');
}
const parcelUrl = countySchema.baseUrls.parcel;
const spatialReference = countySchema.spatialReference;
const schema = countySchema.keys;
const pinKey = schema["pinKey"];
const response = yield axios_1.default.get(`${parcelUrl}?where=${pinKey}=${pinString}&inSR=${spatialReference}&outSR=${spatialReference}&outFields=*&f=pjson`, { httpsAgent: constants_1.agent });
const rings = response.data.features[0].geometry;
//console.log(rings)
// Fetch attributes and geometry
const { features } = yield (0, fetch_parcel_by_polygon_1.fetchParcelByPolygon)(county, parcelUrl, spatialReference, rings, schema, layer);
res.send({
features
});
}
catch (e) {
console.error("Error fetching polyline bounds:", e);
return res.status(500).send("An error occurred while fetching polyline bounds");
}
finally {
//await closeClient();
}
}));
// fetch the county boundary and calculate centroid. The goal for this is to also be able to create an overall scale factor for the county in the future.
router.get('/:state/:county/boundary', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
const { state, county } = req.params;
try {
const countySchema = yield (0, fetch_from_mongo_1.fetchCountySchema)(state, county);
const boundaryUrl = countySchema.baseUrls.boundary;
if (!boundaryUrl) {
console.error("The county schema provided does not contain the overall boundary. Please add it to the schema file if you wish to continue");
return res.status(500).send("The county schema provided does not contain the overall boundary. Please add it to the schema file if you wish to continue");
}
const spatialReference = countySchema.spatialReference;
const url = `${boundaryUrl}?where=1%3D1&inSR=${spatialReference}&outSR=${spatialReference}&outFields=*&f=pjson`;
console.log(url);
const response = yield axios_1.default.get(url, { httpsAgent: constants_1.agent });
const data = response.data;
if (!data.features) {
throw new Error(`No features returned in response: ${JSON.stringify(data, null, 2)}`);
}
const clippedFeatures = data.features.map((feature) => {
const geometry = feature.geometry;
if (!geometry || (!geometry.rings && !geometry.paths)) {
console.warn('Invalid or missing geometry:', JSON.stringify(feature, null, 2));
return null;
}
// Handle the case where geometry.rings is present
if (geometry.rings && geometry.rings.length > 0) {
const firstRing = geometry.rings[0]; // Get only the first ring
const polyline = (0, calculate_centroid_1.convertRingsToPolyline)([firstRing]); // Convert only the first ring to a polyline
const centroid = (0, calculate_centroid_1.calculateCentroid)(polyline); // Calculate centroid of the first ring
return {
attributes: {
LegalDescr: `${county} county centroid X: ${(centroid === null || centroid === void 0 ? void 0 : centroid.x) ? centroid.x : ""} Y: ${(centroid === null || centroid === void 0 ? void 0 : centroid.y) ? centroid.y : ""}`
},
centroid,
geometry: { rings: [firstRing] } // Include only the first ring in the output
};
}
// Handle the case where geometry.paths is present
if (geometry.paths && geometry.paths.length > 0) {
const firstPath = geometry.paths[0]; // Get only the first path
const polyline = (0, calculate_centroid_1.convertRingsToPolyline)([firstPath]); // Convert only the first path to a polyline
const centroid = (0, calculate_centroid_1.calculateCentroid)(polyline); // Calculate centroid of the first path
return {
attributes: {
LegalDescr: `${county} county centroid X: ${(centroid === null || centroid === void 0 ? void 0 : centroid.x) ? centroid.x : ""} Y: ${(centroid === null || centroid === void 0 ? void 0 : centroid.y) ? centroid.y : ""}`
},
centroid,
geometry: { paths: [firstPath] } // Include only the first path in the output
};
}
return null;
}).filter((feature) => { var _a, _b; return feature && feature.geometry && (((_a = feature.geometry.rings) === null || _a === void 0 ? void 0 : _a.length) > 0 || ((_b = feature.geometry.paths) === null || _b === void 0 ? void 0 : _b.length) > 0); });
res.send({ features: clippedFeatures });
}
catch (e) {
console.error("Error fetching polyline bounds:", e);
return res.status(500).send("An error occurred while fetching polyline bounds");
}
}));
router.get('/:state/:county/ngs', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
const baseUrl = 'https://services2.arcgis.com/C8EMgrsFcRFL6LrL/arcgis/rest/services/NGS_Datasheets_Feature_Service/FeatureServer/1/query';
const { state, county } = req.params;
const geometry = req.query.geometry;
if (!geometry) {
return res.status(400).json({ message: 'Geometry box parameter is missing.' });
}
const { xmin, ymin, xmax, ymax } = (0, fetch_parcel_data_by_box_1.parseGeometryBox)(geometry);
if (!xmin || !ymin || !xmax || !ymax) {
return res.status(400).send('Missing required query parameters: xmin, ymin, xmax, ymax');
}
try {
const countySchema = yield (0, fetch_from_mongo_1.fetchCountySchema)(state, county);
const spatialReference = countySchema.spatialReference;
const results = yield (0, fetch_ngs_data_by_box_1.fetchNGSdataByBox)(Number(xmin), Number(ymin), Number(xmax), Number(ymax), baseUrl, spatialReference);
res.json(results);
}
catch (error) {
console.error('Error fetching parcel data:', error);
res.status(500).send('An error occurred while fetching parcel data');
}
}));
router.get('/:state/:county/flood', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
const { state, county } = req.params;
const geometry = req.query.geometry;
let parsedGeometry;
if (!geometry) {
return res.status(400).send('Missing required query parameter: geometry');
}
try {
parsedGeometry = JSON.parse(decodeURIComponent(geometry));
}
catch (e) {
console.error('Error parsing geometry:', e);
return res.status(400).send('Invalid geometry parameter');
}
try {
const countySchema = yield (0, fetch_from_mongo_1.fetchCountySchema)(state, county);
const layersDb = yield (0, database_1.connectToDatabase)('available_layers');
const layerCollection = yield layersDb.collection('layers').findOne({}, { projection: { _id: 0 } });
const layer = layerCollection && layerCollection['flood'] ? layerCollection['flood'] : "0";
console.log(layer);
const floodUrl = 'https://hazards.fema.gov/arcgis/rest/services/public/NFHLWMS/MapServer/28/query';
const spatialReference = countySchema.spatialReference;
const schema = countySchema.keys;
const features = yield (0, fetch_flood_by_post_1.fetchFloodByPost)(county, floodUrl, spatialReference, parsedGeometry, schema, layer);
res.send(features);
}
catch (e) {
console.error("Error fetching polyline bounds:", e);
return res.status(500).send("An error occurred while fetching polyline bounds");
}
finally {
yield (0, database_1.closeClient)();
}
}));
router.get('/:state/:county/zoning', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
const { state, county } = req.params;
const geometry = req.query.geometry;
let parsedGeometry;
if (!geometry) {
return res.status(400).send('Missing required query parameter: geometry');
}
try {
parsedGeometry = JSON.parse(decodeURIComponent(geometry));
}
catch (e) {
console.error('Error parsing geometry:', e);
return res.status(400).send('Invalid geometry parameter');
}
try {
const countySchema = yield (0, fetch_from_mongo_1.fetchCountySchema)(state, county);
const layersDb = yield (0, database_1.connectToDatabase)('available_layers');
const layerCollection = yield layersDb.collection('layers').findOne({}, { projection: { _id: 0 } });
const layer = layerCollection && layerCollection['zoning'] ? layerCollection['zoning'] : "0";
console.log(layer);
const zoningUrl = 'https://www.horrycountysc.gov/parcelapp/rest/services/HorryCountyGISApp/MapServer/30/query';
const spatialReference = countySchema.spatialReference;
const schema = countySchema.keys;
const features = yield (0, fetch_zoning_by_post_1.fetchZoningByPost)(county, zoningUrl, spatialReference, parsedGeometry, schema, layer);
res.send(features);
}
catch (e) {
console.error("Error fetching polyline bounds:", e);
return res.status(500).send("An error occurred while fetching polyline bounds");
}
finally {
yield (0, database_1.closeClient)();
}
}));
exports.default = router;