import { xyInBBox } from './gis.js' import { clone } from './geojson.js' // import simplify from 'https://esm.sh/@turf/simplify' // util has it's own, this used locally in pathFilter function nestedProperty(obj, path) { if (typeof path === 'string') path = path.split('.') return path.reduce((obj, param) => obj[param], obj) } // export function simplifyLineStrings(json, tolerance = 0.01, cloneJson = true) { // if (cloneJson) json = clone(json) // // Process each feature // json.features.forEach(feature => { // // Only process LineString features // if (feature.geometry.type === 'LineString') { // const coords = feature.geometry.coordinates // // Skip if not enough points to simplify // if (coords.length <= 2) { // console.log('coords.length <= 2') // return // } // // Save first and last points // const firstPoint = [...coords[0]] // const lastPoint = [...coords[coords.length - 1]] // // Simplify using turf // const simplified = simplify(feature, { tolerance }) // // Replace the simplified coordinates // feature.geometry = simplified.geometry // // Ensure first and last points are preserved exactly // if (feature.geometry.coordinates.length >= 2) { // feature.geometry.coordinates[0] = firstPoint // feature.geometry.coordinates[ // feature.geometry.coordinates.length - 1 // ] = lastPoint // } // } // }) // return json // } // turf.flatten() // bin/demultline (roads has it's own) export function flattenMultiLineStrings(json, cloneJson = true) { if (cloneJson) json = clone(json) const features = [] json.features.forEach(feature => { if (feature.geometry.type === 'MultiLineString') { feature.geometry.coordinates.forEach(coords => { const copy = Object.assign({}, feature) copy.geometry.type = 'LineString' copy.geometry.coordinates = coords features.push(copy) }) } else { features.push(feature) } }) json.features = features return json } export function bboxFilter(json, bbox, cloneJson = true) { if (cloneJson) json = clone(json) const features = json.features.filter(feature => { let coords = feature.geometry.coordinates coords = coords.filter(pt => xyInBBox(bbox, pt)) // if (coords.length !== feature.geometry.coordinates.length) // console.log( // feature.geometry.coordinates.length, // '->', // coords.length // ) feature.geometry.coordinates = coords return coords.length >= 2 }) json.features = features return json } // ========= REMIND: refactor into bin/ // AS: Used locally only, pathFilter // MB: nmcounties npm script // The most general filter: keeps a feature only if // filterFcn(feature, i, features) returns true. // The filterFcn can also modify feature properties. // json is mutated, equals the return value (sugar) export function featureFilter(json, filterFcn) { json.features = json.features.filter(filterFcn) return json } // Used locally only, geometryFilter & streetsFilter // Filter by a property (path) matching one of values array. export function pathFilter(json, featurePath, values) { if (typeof values === 'string') values = values.split(' ') featureFilter(json, feature => { const value = nestedProperty(feature, featurePath) return values.includes(value) }) return json } // The above specific to geometry (Point, Polygon, LineString) export function geometryFilter(json, values, cloneJson = true) { if (cloneJson) json = clone(json) return pathFilter(json, 'geometry.type', values) } // Reduces the feature properties to only those in the properties array. export function propertiesFilter(json, properties, cloneJson = true) { if (cloneJson) json = clone(json) if (typeof properties === 'string') properties = properties.split(' ') json.features.forEach(feature => { const obj = {} properties.forEach(prop => { if (feature.properties[prop] !== undefined) { obj[prop] = feature.properties[prop] } }) feature.properties = obj }) return json } // AS: bin/json2roads; only used in MB.mkTiles w/ tippecanoe // service highways could be of interest too, esp fire/emergency // "highway": "service", // "service": "xxx" where service is (in santafe zoom 10) // 6 "service": "emergency_access" // 120 "service": "alley" // 141 "service": "drive-through" // 1300 "service": "driveway" // 1506 "service": "parking_aisle" export const osmStreetTypes = [ 'motorway', 'trunk', 'residential', 'primary', 'secondary', 'tertiary', 'motorway_link', 'trunk_link', 'primary_link', 'secondary_link', 'tertiary_link', ] export const osmStreetProperties = [ 'highway', 'oneway', 'name', 'tiger:name_base', ] // A simple filter for streets. // Filters for: // - LineString geometry // - highways matching streetTypes array, defaulted to above // - properties reduced to streetProperties, defaulted to above export function streetsFilter( json, streetTypes = osmStreetTypes, streetProperties = osmStreetProperties, cloneJson = true ) { if (cloneJson) json = clone(json) geometryFilter(json, 'LineString') // see https://wiki.openstreetmap.org/wiki/Highways // note motorway_junction's are Point geometries pathFilter(json, 'properties.highway', streetTypes) propertiesFilter(json, streetProperties) return json }