UNPKG

make-geo-json

Version:

Converts polylines, wkts, wkbs, polygons, circles, rectangles, as well as standard geoJSON into geoJSON MultiPolygon Feature Objects. Converts wkb_list and FeatureCollection to arrays.

408 lines (375 loc) 12.8 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.makeGeoJSON = exports.getArea = exports.polyToMulti = exports.ensureGeometryIsValid = exports.ensureShapeIsClosed = exports.convertPoly = exports.translateGooglePoly = undefined; var _wkx = require('wkx'); var _wkx2 = _interopRequireDefault(_wkx); var _map = require('lodash/map'); var _map2 = _interopRequireDefault(_map); var _flatten = require('lodash/flatten'); var _flatten2 = _interopRequireDefault(_flatten); var _isEqual = require('lodash/isEqual'); var _isEqual2 = _interopRequireDefault(_isEqual); var _isEmpty = require('lodash/isEmpty'); var _isEmpty2 = _interopRequireDefault(_isEmpty); var _cloneDeep = require('lodash/cloneDeep'); var _cloneDeep2 = _interopRequireDefault(_cloneDeep); var _polyline = require('polyline'); var _polyline2 = _interopRequireDefault(_polyline); var _geojsonArea = require('@mapbox/geojson-area'); var _geojsonArea2 = _interopRequireDefault(_geojsonArea); var _geojsonRewind = require('geojson-rewind'); var _geojsonRewind2 = _interopRequireDefault(_geojsonRewind); var _makeCircle = require('./makeCircle'); var _makeCircle2 = _interopRequireDefault(_makeCircle); var _rectFrom2Points = require('./rectFrom2Points'); var _rectFrom2Points2 = _interopRequireDefault(_rectFrom2Points); var _kml = require('./kml'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // receives Google Maps Poly and translates to useable format for validation // convertpoly function takes polyline, geoJSON, wkt, wkb, circle paths, // and rectangle paths // and returns geoJSON // in the form of 'Feature' objects // see https://tools.ietf.org/html/rfc7946 for geoJSONFORMAT const translateGooglePoly = exports.translateGooglePoly = gPoly => { const gPolyClone = (0, _cloneDeep2.default)(gPoly); const coords = (0, _map2.default)(gPolyClone.path, coord => [coord.lng, coord.lat]); const feature = { type: 'Feature', properties: {}, geometry: { type: 'MultiPolygon', coordinates: [[coords]] } }; return feature; }; // given different poly types: polyline, geoJSON (both poly and multipoly), // wkt, wkb, circle (center/radius), rectangle (bounds/path) // Returns as geoJSON Feature Objects const convertPoly = exports.convertPoly = (type, poly) => { const polyClone = (0, _cloneDeep2.default)(poly); switch (type) { case 'googlePoly': { const googPoly = translateGooglePoly(polyClone); return convertPoly('geoJSON', googPoly); } case 'polyline': { const properties = polyClone.properties ? polyClone.properties : {}; const feature = { type: 'Feature', properties, geometry: _polyline2.default.toGeoJSON(polyClone) }; return feature; } case 'wkt': { const properties = polyClone.properties ? polyClone.properties : {}; const feature = { type: 'Feature', properties, geometry: _wkx2.default.Geometry.parse(polyClone).toGeoJSON() }; return feature; } case 'wkb': { const properties = polyClone.properties ? polyClone.properties : {}; const buf = Buffer.from(polyClone, 'base64'); const feature = { type: 'Feature', properties, geometry: _wkx2.default.Geometry.parse(buf).toGeoJSON(polyClone) }; return feature; } case 'polygons': { const properties = polyClone.properties ? polyClone.properties : {}; const pathCoords = (0, _map2.default)(polyClone.path, x => [x.lng, x.lat]); const feature = { type: 'Feature', properties, geometry: { type: 'Polygon', coordinates: [pathCoords] } }; return feature; } case 'circles': { const circleCoords = (0, _map2.default)(polyClone.path, x => [x.lng, x.lat]); const properties = polyClone.properties ? polyClone.properties : {}; const feature = { type: 'Feature', properties, geometry: { type: 'Polygon', coordinates: [circleCoords] } }; return feature; } case 'rectangles': { const properties = polyClone.properties ? polyClone.properties : {}; const { bounds } = polyClone; const rectCoords = [[bounds.west, bounds.north], [bounds.west, bounds.south], [bounds.east, bounds.south], [bounds.east, bounds.north]]; return { type: 'Feature', properties, geometry: { type: 'Polygon', coordinates: [rectCoords] } }; } case 'Feature': { const properties = polyClone.properties ? polyClone.properties : {}; const feature = { type: polyClone.type, properties, geometry: polyClone.geometry }; return feature; } case 'kml': { const properties = polyClone.properties ? polyClone.properties : {}; const feature = { type: polyClone.type, properties, geometry: polyClone.geometry }; return feature; } case 'geoJSON': { const properties = polyClone.properties ? polyClone.properties : {}; const feature = { type: polyClone.type, properties, geometry: polyClone.geometry }; return feature; } case 'geo_json': { const properties = polyClone.properties ? polyClone.properties : {}; const feature = { type: polyClone.type, properties, geometry: polyClone.geometry }; return feature; } default: console.log('INVALID POLY TYPE', JSON.stringify(poly, null, 2)); throw Error(`Ensure Geometry - invalid poly type: ${polyClone.type}`); } }; // given set of coordinates, checks to see if last set is equal to first set // (read: makes sure it's closed) // if not, closes shape by adding first set to end of coordinate array const ensureShapeIsClosed = exports.ensureShapeIsClosed = shape => { const shapeClone = (0, _cloneDeep2.default)(shape); if (shapeClone.length >= 3) { const last = shapeClone.length - 1; if (!(0, _isEqual2.default)(shapeClone[0], shapeClone[last])) shapeClone.push(shapeClone[0]); return shapeClone; } if (shapeClone.length === 1) { const radius = 75; const center = shapeClone[0]; const sides = 24; const coordinates = (0, _makeCircle2.default)(radius, center, sides); const last = coordinates.length - 1; if (!(0, _isEqual2.default)(coordinates[0], coordinates[last])) coordinates.push(coordinates[0]); return coordinates; } if (shapeClone.length === 2) { const p1 = shapeClone[0]; const p2 = shapeClone[1]; return (0, _rectFrom2Points2.default)(p1, p2); } if (shapeClone.length === 0) { return shapeClone; } return shapeClone; }; // given geoJSON FEATURE OBJ, calls ensureShapeIsClosed() to check coord // validity const ensureGeometryIsValid = exports.ensureGeometryIsValid = featObj => { const featObjClone = (0, _cloneDeep2.default)(featObj); switch (featObjClone.geometry.type) { case 'Point': { featObjClone.geometry.type = 'Polygon'; const coords = [featObjClone.geometry.coordinates]; featObjClone.geometry.coordinates = [ensureShapeIsClosed(coords)]; return featObjClone; } case 'MultiPoint': { featObjClone.geometry.type = 'Polygon'; featObjClone.geometry.coordinates = [ensureShapeIsClosed(featObjClone.geometry.coordinates)]; return featObjClone; } case 'LineString': { featObjClone.geometry.type = 'Polygon'; featObjClone.geometry.coordinates = [ensureShapeIsClosed(featObjClone.geometry.coordinates)]; return featObjClone; } case 'MultiLineString': { const coordsArray = (0, _flatten2.default)(featObjClone.geometry.coordinates); featObjClone.geometry.coordinates = [ensureShapeIsClosed(coordsArray)]; return featObjClone; } case 'Polygon': { const poly = featObjClone.geometry.coordinates; const data = (0, _map2.default)(poly, shape => ensureShapeIsClosed(shape)); featObjClone.geometry.coordinates = data; return featObjClone; } case 'MultiPolygon': { const data = featObjClone.geometry.coordinates; const carl = (0, _map2.default)(data, poly => (0, _map2.default)(poly, shape => ensureShapeIsClosed(shape))); featObjClone.geometry.coordinates = carl; return featObjClone; } default: throw Error(`Ensure Geometry - invalid geometry type: ${featObjClone.geometry.type}`); } }; // Given geoJSON Feature // Returns Polygons as MultiPolys, // Multipolys pass through // Everything else is error const polyToMulti = exports.polyToMulti = geoJSONObj => { const gJClone = (0, _cloneDeep2.default)(geoJSONObj); const { properties } = gJClone; const { type, coordinates } = gJClone.geometry; try { if ((0, _isEmpty2.default)(gJClone.geometry.coordinates)) { const errorFeature = { type: 'Feature', properties, geometry: { type: 'ERROR', coordinates } }; return errorFeature; } switch (type) { case 'Polygon': { const coordArray = gJClone.geometry.coordinates; if (coordArray[0][0].length > 1) { const newFeature = { type: 'Feature', properties, geometry: { type: 'MultiPolygon', coordinates: [coordinates] } }; return newFeature; }if (coordArray[0][0] <= 1) { const newFeature = { type: 'Feature', properties, geometry: { type: 'MultiPolygon', coordinates } }; return newFeature; } break; } case 'MultiLineString': { const newFeature = { type: 'Feature', properties, geometry: { type: 'MultiPolygon', coordinates: [coordinates] } }; return newFeature; } case 'MultiPolygon': { return gJClone; } default: throw Error(`Ensure Geometry - invalid poly type: ${type}`); } } catch (error) { console.error(error); throw Error(`Ensure Geometry - invalid poly type: ${type}`); } throw Error(`Unknown Geometry Error - invalid poly type: ${type}`); }; // Given a GeoJSON Feature Object, returns the area inside the properties obj const getArea = exports.getArea = featObj => { const featObjClone = (0, _cloneDeep2.default)(featObj); const { geometry } = featObjClone; const areaInMeters = _geojsonArea2.default.geometry(geometry); const area = areaInMeters * 0.0000003861; featObjClone.properties.area = area; return featObjClone; }; // Given one of our specified poly types, runs it through the series of // functions above // Returns valid geoJSON Feature // Or array of features if given FeatureCollection, KML const makeGeoJSON = exports.makeGeoJSON = (type, poly) => { const polyClone = (0, _cloneDeep2.default)(poly); if (type !== 'kml' && type !== 'FeatureCollection' && type !== 'wkb_list') { const featObj = convertPoly(type, polyClone); const validatedObj = ensureGeometryIsValid(featObj); const resizedArray = polyToMulti(validatedObj); const woundCoords = (0, _geojsonRewind2.default)(resizedArray); const resizedWithArea = getArea(woundCoords); return resizedWithArea; } if (type === 'kml') { const preppedKML = (0, _kml.prepKml)(polyClone); const resultsArray = []; for (let i = 0; i < preppedKML.length; i += 1) { resultsArray.push(makeGeoJSON(preppedKML[i].type, preppedKML[i])); } return resultsArray; } if (type === 'FeatureCollection') { const resultsArray = []; for (let i = 0; i < polyClone.length; i += 1) { resultsArray.push(makeGeoJSON(polyClone[i].type, polyClone[i])); } return resultsArray; } if (type === 'wkb_list') { const resultsArray = []; for (let i = 0; i < polyClone.length; i += 1) { resultsArray.push(makeGeoJSON('wkb', polyClone[i])); } return resultsArray; } throw Error(`${type} not currently supported.`); }; /* if type is array, iterate return array * if type is object, return object */