@rapideditor/location-conflation
Version:
Define complex geographic regions by including and excluding country codes and geojson shapes
8 lines (7 loc) • 19.3 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../index.mjs"],
"sourcesContent": ["import * as CountryCoder from '@rapideditor/country-coder';\nimport * as Polyclip from 'polyclip-ts';\n\nimport calcArea from '@mapbox/geojson-area';\nimport circleToPolygon from 'circle-to-polygon';\nimport precision from 'geojson-precision';\nimport prettyStringify from '@aitodotai/json-stringify-pretty-compact';\n\n\nexport class LocationConflation {\n\n // constructor\n //\n // `fc` Optional FeatureCollection of known features\n //\n // Optionally pass a GeoJSON FeatureCollection of known features which we can refer to later.\n // Each feature must have a filename-like `id`, for example: `something.geojson`\n //\n // {\n // \"type\": \"FeatureCollection\"\n // \"features\": [\n // {\n // \"type\": \"Feature\",\n // \"id\": \"philly_metro.geojson\",\n // \"properties\": { \u2026 },\n // \"geometry\": { \u2026 }\n // }\n // ]\n // }\n constructor(fc) {\n // The _cache retains resolved features, so if you ask for the same thing multiple times\n // we don't repeat the expensive resolving/clipping operations.\n //\n // Each feature has a stable identifier that is used as the cache key.\n // The identifiers look like:\n // - for point locations, the stringified point: e.g. '[8.67039,49.41882]'\n // - for geojson locations, the geojson id: e.g. 'de-hamburg.geojson'\n // - for countrycoder locations, feature.id property: e.g. 'Q2' (countrycoder uses Wikidata identifiers)\n // - for aggregated locationSets, +[include]-[exclude]: e.g '+[Q2]-[Q18,Q27611]'\n this._cache = {};\n\n // When strict mode = true, throw on invalid locations or locationSets.\n // When strict mode = false, return `null` for invalid locations or locationSets.\n this.strict = true;\n\n // process input FeatureCollection\n if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {\n fc.features.forEach(feature => {\n feature.properties = feature.properties || {};\n let props = feature.properties;\n\n // Get `id` from either `id` or `properties`\n let id = feature.id || props.id;\n if (!id || !/^\\S+\\.geojson$/i.test(id)) return;\n\n // Ensure `id` exists and is lowercase\n id = id.toLowerCase();\n feature.id = id;\n props.id = id;\n\n // Ensure `area` property exists\n if (!props.area) {\n const area = calcArea.geometry(feature.geometry) / 1e6; // m\u00B2 to km\u00B2\n props.area = Number(area.toFixed(2));\n }\n\n this._cache[id] = feature;\n });\n }\n\n // Replace CountryCoder world geometry to be a polygon covering the world.\n let world = _cloneDeep(CountryCoder.feature('Q2'));\n world.geometry = {\n type: 'Polygon',\n coordinates: [[[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]]\n };\n world.id = 'Q2';\n world.properties.id = 'Q2';\n world.properties.area = calcArea.geometry(world.geometry) / 1e6; // m\u00B2 to km\u00B2\n this._cache.Q2 = world;\n }\n\n\n // validateLocation\n // `location` The location to validate\n //\n // Pass a `location` value to validate\n //\n // Returns a result like:\n // {\n // type: 'point', 'geojson', or 'countrycoder'\n // location: the queried location\n // id: the stable identifier for the feature\n // }\n // or `null` if the location is invalid\n //\n validateLocation(location) {\n if (Array.isArray(location) && (location.length === 2 || location.length === 3)) { // [lon, lat] or [lon, lat, radius] point?\n const lon = location[0];\n const lat = location[1];\n const radius = location[2];\n if (\n Number.isFinite(lon) && lon >= -180 && lon <= 180 &&\n Number.isFinite(lat) && lat >= -90 && lat <= 90 &&\n (location.length === 2 || (Number.isFinite(radius) && radius > 0))\n ) {\n const id = '[' + location.toString() + ']';\n return { type: 'point', location: location, id: id };\n }\n\n } else if (typeof location === 'string' && /^\\S+\\.geojson$/i.test(location)) { // a .geojson filename?\n const id = location.toLowerCase();\n if (this._cache[id]) {\n return { type: 'geojson', location: location, id: id };\n }\n\n } else if (typeof location === 'string' || typeof location === 'number') { // a country-coder value?\n const feature = CountryCoder.feature(location);\n if (feature) {\n // Use wikidata QID as the identifier, since that seems to be the one\n // property that everything in CountryCoder is guaranteed to have.\n const id = feature.properties.wikidata;\n return { type: 'countrycoder', location: location, id: id };\n }\n }\n\n if (this.strict) {\n throw new Error(`validateLocation: Invalid location: \"${location}\".`);\n } else {\n return null;\n }\n }\n\n\n // resolveLocation\n // `location` The location to resolve\n //\n // Pass a `location` value to resolve\n //\n // Returns a result like:\n // {\n // type: 'point', 'geojson', or 'countrycoder'\n // location: the queried location\n // id: a stable identifier for the feature\n // feature: the resolved GeoJSON feature\n // }\n // or `null` if the location is invalid\n //\n resolveLocation(location) {\n const valid = this.validateLocation(location);\n if (!valid) return null;\n\n const id = valid.id;\n\n // Return a result from cache if we can\n if (this._cache[id]) {\n return Object.assign(valid, { feature: this._cache[id] });\n }\n\n // A [lon,lat] coordinate pair?\n if (valid.type === 'point') {\n const lon = location[0];\n const lat = location[1];\n const radius = location[2] || 25; // km\n const EDGES = 10;\n const PRECISION = 3;\n const area = Math.PI * radius * radius;\n const feature = this._cache[id] = precision({\n type: 'Feature',\n id: id,\n properties: { id: id, area: Number(area.toFixed(2)) },\n geometry: circleToPolygon([lon, lat], radius * 1000, EDGES) // km to m\n }, PRECISION);\n return Object.assign(valid, { feature: feature });\n\n // A .geojson filename?\n } else if (valid.type === 'geojson') {\n // nothing to do here - these are all in _cache and would have returned already\n\n // A country-coder identifier?\n } else if (valid.type === 'countrycoder') {\n let feature = _cloneDeep(CountryCoder.feature(id));\n let props = feature.properties;\n\n // -> This block of code is weird and requires some explanation. <-\n // CountryCoder includes higher level features which are made up of members.\n // These features don't have their own geometry, but CountryCoder provides an\n // `aggregateFeature` method to combine these members into a MultiPolygon.\n // In the past, Turf/JSTS/martinez could not handle the aggregated features,\n // so we'd iteratively union them all together. (this was slow)\n // But now mfogel/polygon-clipping handles these MultiPolygons like a boss.\n // This approach also has the benefit of removing all the internal boaders and\n // simplifying the regional polygons a lot.\n if (Array.isArray(props.members)) {\n let aggregate = CountryCoder.aggregateFeature(id);\n aggregate.geometry.coordinates = _clip([aggregate], 'UNION').geometry.coordinates;\n feature.geometry = aggregate.geometry;\n }\n\n // Ensure `area` property exists\n if (!props.area) {\n const area = calcArea.geometry(feature.geometry) / 1e6; // m\u00B2 to km\u00B2\n props.area = Number(area.toFixed(2));\n }\n\n // Ensure `id` property exists\n feature.id = id;\n props.id = id;\n\n this._cache[id] = feature;\n return Object.assign(valid, { feature: feature });\n }\n\n if (this.strict) {\n throw new Error(`resolveLocation: Couldn't resolve location \"${location}\".`);\n } else {\n return null;\n }\n }\n\n\n // validateLocationSet\n // `locationSet` the locationSet to validate\n //\n // Pass a locationSet Object to validate like:\n // {\n // include: [ Array of locations ],\n // exclude: [ Array of locations ]\n // }\n //\n // Returns a result like:\n // {\n // type: 'locationset'\n // locationSet: the queried locationSet\n // id: the stable identifier for the feature\n // }\n // or `null` if the locationSet is invalid\n //\n validateLocationSet(locationSet) {\n locationSet = locationSet || {};\n const validator = this.validateLocation.bind(this);\n let include = (locationSet.include || []).map(validator).filter(Boolean);\n let exclude = (locationSet.exclude || []).map(validator).filter(Boolean);\n\n if (!include.length) {\n if (this.strict) {\n throw new Error(`validateLocationSet: LocationSet includes nothing.`);\n } else {\n // non-strict mode, replace an empty locationSet with one that includes \"the world\"\n locationSet.include = ['Q2'];\n include = [{ type: 'countrycoder', location: 'Q2', id: 'Q2' }];\n }\n }\n\n // Generate stable identifier\n include.sort(_sortLocations);\n let id = '+[' + include.map(d => d.id).join(',') + ']';\n if (exclude.length) {\n exclude.sort(_sortLocations);\n id += '-[' + exclude.map(d => d.id).join(',') + ']';\n }\n\n return { type: 'locationset', locationSet: locationSet, id: id };\n }\n\n\n // resolveLocationSet\n // `locationSet` the locationSet to resolve\n //\n // Pass a locationSet Object to validate like:\n // {\n // include: [ Array of locations ],\n // exclude: [ Array of locations ]\n // }\n //\n // Returns a result like:\n // {\n // type: 'locationset'\n // locationSet: the queried locationSet\n // id: the stable identifier for the feature\n // feature: the resolved GeoJSON feature\n // }\n // or `null` if the locationSet is invalid\n //\n resolveLocationSet(locationSet) {\n locationSet = locationSet || {};\n const valid = this.validateLocationSet(locationSet);\n if (!valid) return null;\n\n const id = valid.id;\n\n // Return a result from cache if we can\n if (this._cache[id]) {\n return Object.assign(valid, { feature: this._cache[id] });\n }\n\n const resolver = this.resolveLocation.bind(this);\n const includes = (locationSet.include || []).map(resolver).filter(Boolean);\n const excludes = (locationSet.exclude || []).map(resolver).filter(Boolean);\n\n // Return quickly if it's a single included location..\n if (includes.length === 1 && excludes.length === 0) {\n return Object.assign(valid, { feature: includes[0].feature });\n }\n\n // Calculate unions\n const includeGeoJSON = _clip(includes.map(d => d.feature), 'UNION');\n const excludeGeoJSON = _clip(excludes.map(d => d.feature), 'UNION');\n\n // Calculate difference, update `area` and return result\n let resultGeoJSON = excludeGeoJSON ? _clip([includeGeoJSON, excludeGeoJSON], 'DIFFERENCE') : includeGeoJSON;\n const area = calcArea.geometry(resultGeoJSON.geometry) / 1e6; // m\u00B2 to km\u00B2\n resultGeoJSON.id = id;\n resultGeoJSON.properties = { id: id, area: Number(area.toFixed(2)) };\n\n this._cache[id] = resultGeoJSON;\n return Object.assign(valid, { feature: resultGeoJSON });\n }\n\n\n // stringify\n // convenience method to prettyStringify the given object\n stringify(obj, options) {\n return prettyStringify(obj, options);\n }\n}\n\n\n// Wrap the https://github.com/luizbarboza/polyclip-ts library and return a GeoJSON feature.\nfunction _clip(features, which) {\n if (!Array.isArray(features) || !features.length) return null;\n\n const fn = { UNION: Polyclip.union, DIFFERENCE: Polyclip.difference }[which];\n const args = features.map(feature => feature.geometry.coordinates);\n const coords = fn.apply(null, args);\n return {\n type: 'Feature',\n properties: {},\n geometry: {\n type: whichType(coords),\n coordinates: coords\n }\n };\n\n // is this a Polygon or a MultiPolygon?\n function whichType(coords) {\n const a = Array.isArray(coords);\n const b = a && Array.isArray(coords[0]);\n const c = b && Array.isArray(coords[0][0]);\n const d = c && Array.isArray(coords[0][0][0]);\n return d ? 'MultiPolygon' : 'Polygon';\n }\n}\n\n\nfunction _cloneDeep(obj) {\n return JSON.parse(JSON.stringify(obj));\n}\n\n\n// Sorting the location lists is ok because they end up unioned together.\n// This sorting makes it possible to generate a deterministic id.\nfunction _sortLocations(a, b) {\n const rank = { countrycoder: 1, geojson: 2, point: 3 };\n const aRank = rank[a.type];\n const bRank = rank[b.type];\n\n return (aRank > bRank) ? 1\n : (aRank < bRank) ? -1\n : a.id.localeCompare(b.id);\n}\n\nexport default LocationConflation;\n"],
"mappings": "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",
"names": ["calcArea", "precision", "circleToPolygon", "prettyStringify", "coords"]
}