ExtraFunctions: closestN now can work with multiple layers to pick from

This commit is contained in:
Pieter Vander Vennet 2024-01-16 04:12:13 +01:00
parent 593d7bd07a
commit c298e16f02

View file

@ -15,7 +15,7 @@ export interface ExtraFuncParams {
*/ */
getFeaturesWithin: ( getFeaturesWithin: (
layerId: string, layerId: string,
bbox: BBox bbox: BBox,
) => Feature<Geometry, Record<string, string>>[][] ) => Feature<Geometry, Record<string, string>>[][]
getFeatureById: (id: string) => Feature<Geometry, Record<string, string>> getFeatureById: (id: string) => Feature<Geometry, Record<string, string>>
} }
@ -71,7 +71,7 @@ class EnclosingFunc implements ExtraFunction {
if ( if (
GeoOperations.completelyWithin( GeoOperations.completelyWithin(
<Feature>feat, <Feature>feat,
<Feature<Polygon | MultiPolygon, any>>otherFeature <Feature<Polygon | MultiPolygon, any>>otherFeature,
) )
) { ) {
result.push({ feat: otherFeature }) result.push({ feat: otherFeature })
@ -162,7 +162,7 @@ class IntersectionFunc implements ExtraFunction {
for (const otherFeature of otherFeatures) { for (const otherFeature of otherFeatures) {
const intersections = GeoOperations.LineIntersections( const intersections = GeoOperations.LineIntersections(
feat, feat,
<Feature<any, Record<string, string>>>otherFeature <Feature<any, Record<string, string>>>otherFeature,
) )
if (intersections.length === 0) { if (intersections.length === 0) {
continue continue
@ -192,7 +192,7 @@ class DistanceToFunc implements ExtraFunction {
// Feature._lon and ._lat is conveniently place by one of the other metatags // Feature._lon and ._lat is conveniently place by one of the other metatags
return GeoOperations.distanceBetween( return GeoOperations.distanceBetween(
[arg0, lat], [arg0, lat],
GeoOperations.centerpointCoordinates(feature) GeoOperations.centerpointCoordinates(feature),
) )
} }
if (typeof arg0 === "string") { if (typeof arg0 === "string") {
@ -207,7 +207,7 @@ class DistanceToFunc implements ExtraFunction {
// arg0 is probably a geojsonfeature // arg0 is probably a geojsonfeature
return GeoOperations.distanceBetween( return GeoOperations.distanceBetween(
GeoOperations.centerpointCoordinates(arg0), GeoOperations.centerpointCoordinates(arg0),
GeoOperations.centerpointCoordinates(feature) GeoOperations.centerpointCoordinates(feature),
) )
} }
} }
@ -252,22 +252,29 @@ class ClosestNObjectFunc implements ExtraFunction {
static GetClosestNFeatures( static GetClosestNFeatures(
params: ExtraFuncParams, params: ExtraFuncParams,
feature: any, feature: any,
features: string | Feature[], features: string | string[] | Feature[],
options?: { maxFeatures?: number; uniqueTag?: string | undefined; maxDistance?: number } options?: { maxFeatures?: number; uniqueTag?: string | undefined; maxDistance?: number },
): { feat: any; distance: number }[] { ): { feat: any; distance: number }[] {
const maxFeatures = options?.maxFeatures ?? 1 const maxFeatures = options?.maxFeatures ?? 1
const maxDistance = options?.maxDistance ?? 500 const maxDistance = options?.maxDistance ?? 500
const uniqueTag: string | undefined = options?.uniqueTag const uniqueTag: string | undefined = options?.uniqueTag
let allFeatures: Feature[][] let allFeatures: Feature[][]
if (typeof features === "string") { if (typeof features === "string") {
const name = features features = [features]
} else {
allFeatures = []
for (const spec of features) {
if (typeof spec === "string") {
const name = spec
const bbox = GeoOperations.bbox( const bbox = GeoOperations.bbox(
GeoOperations.buffer(GeoOperations.bbox(feature), maxDistance) GeoOperations.buffer(GeoOperations.bbox(feature), maxDistance),
) )
const coors = <[number, number][]>bbox.geometry.coordinates const coors = <[number, number][]>bbox.geometry.coordinates
allFeatures = params.getFeaturesWithin(name, new BBox(coors)) allFeatures.push(...params.getFeaturesWithin(name, new BBox(coors)))
} else { } else {
allFeatures = [features] allFeatures.push([spec])
}
}
} }
if (features === undefined) { if (features === undefined) {
return return
@ -278,6 +285,9 @@ class ClosestNObjectFunc implements ExtraFunction {
for (const feats of allFeatures) { for (const feats of allFeatures) {
for (const otherFeature of feats) { for (const otherFeature of feats) {
if (otherFeature.properties === undefined) {
console.warn("OtherFeature does not have properties:", otherFeature)
}
if ( if (
otherFeature === feature || otherFeature === feature ||
otherFeature.properties.id === feature.properties.id otherFeature.properties.id === feature.properties.id
@ -286,14 +296,14 @@ class ClosestNObjectFunc implements ExtraFunction {
} }
const distance = GeoOperations.distanceBetween( const distance = GeoOperations.distanceBetween(
GeoOperations.centerpointCoordinates(otherFeature), GeoOperations.centerpointCoordinates(otherFeature),
selfCenter selfCenter,
) )
if (distance === undefined || distance === null || isNaN(distance)) { if (distance === undefined || distance === null || isNaN(distance)) {
console.error( console.error(
"Could not calculate the distance between", "Could not calculate the distance between",
feature, feature,
"and", "and",
otherFeature otherFeature,
) )
throw "Undefined distance!" throw "Undefined distance!"
} }
@ -303,7 +313,7 @@ class ClosestNObjectFunc implements ExtraFunction {
"Got a suspiciously zero distance between", "Got a suspiciously zero distance between",
otherFeature, otherFeature,
"and self-feature", "and self-feature",
feature feature,
) )
} }
@ -430,7 +440,7 @@ class GetParsed implements ExtraFunction {
return parsed return parsed
} catch (e) { } catch (e) {
console.warn( console.warn(
"Could not parse property " + key + " due to: " + e + ", the value is " + value "Could not parse property " + key + " due to: " + e + ", the value is " + value,
) )
return undefined return undefined
} }
@ -454,15 +464,15 @@ export class ExtraFunctions {
]), ]),
"To enable this feature, add a field `calculatedTags` in the layer object, e.g.:", "To enable this feature, add a field `calculatedTags` in the layer object, e.g.:",
"````", "````",
'"calculatedTags": [', "\"calculatedTags\": [",
' "_someKey=javascript-expression (lazy execution)",', " \"_someKey=javascript-expression (lazy execution)\",",
' "_some_other_key:=javascript expression (strict execution)', " \"_some_other_key:=javascript expression (strict execution)",
' "name=feat.properties.name ?? feat.properties.ref ?? feat.properties.operator",', " \"name=feat.properties.name ?? feat.properties.ref ?? feat.properties.operator\",",
" \"_distanceCloserThen3Km=distanceTo(feat)( some_lon, some_lat) < 3 ? 'yes' : 'no'\" ", " \"_distanceCloserThen3Km=distanceTo(feat)( some_lon, some_lat) < 3 ? 'yes' : 'no'\" ",
" ]", " ]",
"````", "````",
"", "",
"By using `:=` as separator, the attribute will be calculated as soone as the data is loaded (strict evaluation)", "By using `:=` as separator, the attribute will be calculated as soon as the data is loaded (strict evaluation)",
"The default behaviour, using `=` as separator, is lazy loading", "The default behaviour, using `=` as separator, is lazy loading",
"", "",
"The above code will be executed for every feature in the layer. The feature is accessible as `feat` and is an amended geojson object:", "The above code will be executed for every feature in the layer. The feature is accessible as `feat` and is an amended geojson object:",
@ -496,7 +506,7 @@ export class ExtraFunctions {
] ]
public static constructHelpers( public static constructHelpers(
params: ExtraFuncParams params: ExtraFuncParams,
): Record<ExtraFuncType, (feature: Feature) => Function> { ): Record<ExtraFuncType, (feature: Feature) => Function> {
const record: Record<string, (feature: GeoJSONFeature) => Function> = {} const record: Record<string, (feature: GeoJSONFeature) => Function> = {}
for (const f of ExtraFunctions.allFuncs) { for (const f of ExtraFunctions.allFuncs) {