UNPKG

@svizzle/geo

Version:

Svizzle Geo contains geography related helpers.

349 lines (333 loc) 8.88 kB
/** * @module @svizzle/geo/geojson */ import bbox from '@turf/bbox'; import centroid from '@turf/centroid'; import {featureCollection} from '@turf/helpers'; import truncate from '@turf/truncate'; import * as _ from 'lamb'; /** * Return or create the {@link https://tools.ietf.org/html/rfc7946#section-5|bbox} of the provided geojson * * @function * @arg {object} geojson - Geojson object * @return {array} * * @example > getOrMakeBBox({ type: 'FeatureCollection', features: [{ type: 'Feature', geometry: { type: 'Polygon', coordinates: [ [[1, -1], [1, 1], [-1, 1], [-1, -1], [1, -1]] ] } }, { type: 'Feature', geometry: { type: 'Polygon', coordinates: [ [[2, -1], [2, 1], [0, 1], [0, -1], [2, -1]] ] } }] }) [-1, -1, 2, 1] > // no calculation involved here > getOrMakeBBox({ type: 'FeatureCollection', bbox: [-10.0, -10.0, 10.0, 10.0], geometry: { type: 'Polygon', coordinates: [ [ [-10.0, -10.0], [10.0, -10.0], [10.0, 10.0], [-10.0, -10.0] ] ] } }) [-10.0, -10.0, 10.0, 10.0] * @since 0.1.0 */ export const getOrMakeBBox = json => json.bbox ? json.bbox : bbox(json); /** * Return a function expecting a geojson and creating or updating the provided property of all features using the provided map. * Note that you can pass a `key or an alternative key `key_alt` e.g. when you use ISO Alpha 2 codes and you need to identify unrecognized territories with another key. * * @function * @arg {object} args - Geojson object * @arg {string} args.key_alt - Alternative key to be found in properties in `key` is not found. * @arg {string} args.key - Key to be found in properties * @arg {object} args.map - Mapping key (string) -> string * @arg {function} args.mapFn - Function key (string) -> string * @arg {string} args.propName - Name of the property to be added to `properties` * @return {function} - Object -> Object * * @example > geojson = { type: 'FeatureCollection', features: [{ type: 'Feature', geometry: { type: 'Polygon', coordinates: [ [[1, -1], [1, 1], [-1, 1], [-1, -1], [1, -1]] ] }, properties: {iso_a2: 'BF'} }, { type: 'Feature', geometry: { type: 'Polygon', coordinates: [ [[2, -1], [2, 1], [0, 1], [0, -1], [2, -1]] ] }, properties: {name: 'Kosovo'} }, { type: 'Feature', geometry: { type: 'Polygon', coordinates: [ [[4, -1], [2, 7], [0, 5], [0, -4], [4, -1]] ] }, properties: {iso_a2: 'FR'} }] } > keyToColor = {BF: 'red', Kosovo: 'yellow'} > addColor = makeAddFeaturesProperty({ propName: 'color', map: keyToColor, key: 'iso_a2', key_alt: 'name' }) > coloredFeatures = addColor(geojson) { type: 'FeatureCollection', features: [{ type: 'Feature', geometry: { type: 'Polygon', coordinates: [ [[1, -1], [1, 1], [-1, 1], [-1, -1], [1, -1]] ] }, properties: {iso_a2: 'BF', color: 'red'} }, { type: 'Feature', geometry: { type: 'Polygon', coordinates: [ [[2, -1], [2, 1], [0, 1], [0, -1], [2, -1]] ] }, properties: {name: 'Kosovo', color: 'yellow'} }, { type: 'Feature', geometry: { type: 'Polygon', coordinates: [ [[4, -1], [2, 7], [0, 5], [0, -4], [4, -1]] ] }, properties: {iso_a2: 'FR', color: undefined} }] } * @since 0.5.0 */ export const makeUpdateFeaturesProperty = ({ key_alt, key, map, mapFn, propName, }) => _.updateKey('features', _.mapWith( _.updateKey('properties', properties => { let propValue; if (map) { propValue = _.has(map, properties[key]) ? map[properties[key]] : _.has(map, properties[key_alt]) ? map[properties[key_alt]] : undefined } else if (mapFn) { propValue = properties[key] ? mapFn(properties[key]) : properties[key_alt] ? mapFn(properties[key_alt]) : undefined } return { ...properties, [propName]: propValue } }) )); /** * Return the a collection of centroids of the provided features, each having the correspondent feature properties. * * @function * @arg {array} features - Array of features * @return {object} collection - FeatureCollection of Point features * * @example > makeCentroids([ { type: 'Feature', properties: {foo: 'a'}, geometry: {type: 'LineString', coordinates: [ [[1, -1], [1, 1], [-1, 1], [-1, -1], [1, -1]] ]} }, { type: 'Feature', properties: {foo: 'b'}, geometry: {type: 'LineString', coordinates: [ [[2, -1], [2, 1], [0, 1], [0, -1], [2, -1]] ]} } ]) { type: 'FeatureCollection', features: [{ type: 'Feature', geometry: {type: 'Point', coordinates: [0.2, -0.2]}, properties: {foo: 'a'} }, { type: 'Feature', geometry: {type: 'Point', coordinates: [1.2, -0.2]}, properties: {foo: 'b'} }] } * @since 0.1.0 */ export const makeCentroids = _.pipe([ _.mapWith(feature => centroid(feature, {properties: feature.properties})), featureCollection ]); /** * Return a function expecting an object and returning it as a Point feature. * You can define a coordPicker using {@link makeKeysGetter}: * const getCoordinates = makeKeysGetter(['lng', 'lat']) * * @function * @arg {function} coordPicker - The function to create the point coordinates ([longitude, latitude]) from the provided feature * @arg {function} propsTransformer - The function to create the properties of the resulting point from the provided feature * @return {object} point - Geojson Point feature. * * @example > coordPicker = _.collect([_.getKey('lng'), _.getKey('lat')]) > toPointFeature = makeToPointFeature(coordPicker) > toPointFeature({foo: 'a', lng: 0.1, lat: 0.1}) { type: 'Feature', geometry: {type: 'Point', coordinates: [0.1, 0.1]}, properties: {foo: 'a', lng: 0.1, lat: 0.1} } > const propsTransformer = applyFnMap({name: _.getKey('foo')}) > const toPointFeature = makeToPointFeature(coordPicker, propsTransformer) > toPointFeature({foo: 'a', lng: 0.1, lat: 0.1}) { type: 'Feature', geometry: {type: 'Point', coordinates: [0.1, 0.1]}, properties: {name: 'a'} } * @since 0.1.0 */ export const makeToPointFeature = (coordPicker, propsTransformer = null) => object => ({ type: 'Feature', geometry: { type: 'Point', coordinates: coordPicker(object) }, properties: propsTransformer ? propsTransformer(object) : object }); /** * Return a function expecting an array of objects and returning them as a FeatureCollection of Point features. * You can define a coordPicker using {@link makeKeysGetter}: * const getCoordinates = makeKeysGetter(['lng', 'lat']) * * @function * @arg {function} coordPicker - The function to create the point coordinates ([longitude, latitude]) from the provided features * @arg {function} propsTransformer - The function to create the properties of the resulting points from the provided features * @return {object} collection - FeatureCollection of Point features * * @example > coordPicker = _.collect([_.getKey('lng'), _.getKey('lat')]) > toGeoPoints = makeToGeoPoints(coordPicker) > toGeoPoints([ {foo: 'a', lng: 0.1, lat: 0.1}, {foo: 'b', lng: 0.2, lat: 0.2} ]) { type: 'FeatureCollection', features: [{ type: 'Feature', geometry: {type: 'Point', coordinates: [0.1, 0.1]}, properties: {foo: 'a', lng: 0.1, lat: 0.1} }, { type: 'Feature', geometry: {type: 'Point', coordinates: [0.2, 0.2]}, properties: {foo: 'b', lng: 0.2, lat: 0.2} }] } > propsTransformer = applyFnMap({name: _.getKey('foo')}) > toGeoPoints = makeToGeoPoints(coordPicker, propsTransformer) > toGeoPoints([ {foo: 'a', lng: 0.1, lat: 0.1}, {foo: 'b', lng: 0.2, lat: 0.2} ]) { type: 'FeatureCollection', features: [{ type: 'Feature', geometry: {type: 'Point', coordinates: [0.1, 0.1]}, properties: {name: 'a'} }, { type: 'Feature', geometry: {type: 'Point', coordinates: [0.2, 0.2]}, properties: {name: 'b'} }] } * @since 0.1.0 */ export const makeToGeoPoints = (coordPicker, propsTransformer) => _.pipe([ _.mapWith(makeToPointFeature(coordPicker, propsTransformer)), featureCollection ]); // TODO use a reduce to include only items with lat/lng as defined by coordPicker /** * Return a function returning a copy of the provided geojson having the geometry coordinates rounded to the given precision. * * @function * @arg {number} precision - coordinate decimal precision * @return {function} - Geojson -> Geojson * * @example > truncateGeometry = setGeometryPrecision(4) > point = { type: 'Feature', geometry: {type: 'Point', coordinates: [0.1234567, 0.12341]}, properties: {name: 'a'} } > truncateGeometry(point) { type: 'Feature', geometry: {type: 'Point', coordinates: [0.1234, 0.1234]}, properties: {name: 'a'} } * @since 0.1.0 */ export const setGeometryPrecision = precision => geojson => truncate(geojson, {precision, mutate: false}); // convenience function export const truncateGeojson = setGeometryPrecision(4); // TODO DOC: define FeatureCollection type