Update of latlon2country and use its async interface; small refactoring of simplemetagging, improvements to cacheBuilder which respects isShown and calculated tags now

This commit is contained in:
Pieter Vander Vennet 2021-12-07 02:22:56 +01:00
parent e053e9f279
commit 9cfb7fbe68
14 changed files with 417 additions and 4320 deletions

View file

@ -68,7 +68,7 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
const features: { feature: any; freshness: Date }[] = this.upstream.features.data;
const newFeatures = features.filter((f) => {
self.registerCallback(f.feature, layer.layerDef)
self.registerCallback(f.feature)
if (
this.state.selectedElement.data?.id === f.feature.id ||
@ -105,15 +105,18 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
this._is_dirty.setData(false)
}
private registerCallback(feature: any, layer: LayerConfig) {
const src = this.state.allElements.addOrGetElement(feature)
private registerCallback(feature: any) {
const src = this.state?.allElements?.addOrGetElement(feature)
if(src == undefined){
return
}
if (this._alreadyRegistered.has(src)) {
return
}
this._alreadyRegistered.add(src)
const self = this;
src.addCallbackAndRunD(isShown => {
src.addCallbackAndRunD(_ => {
self._is_dirty.setData(true)
})
}

View file

@ -86,6 +86,10 @@ export default class TiledFeatureSource implements Tiled, IndexedFeatureSource,
}
public static createHierarchy(features: FeatureSource, options?: TiledFeatureSourceOptions): TiledFeatureSource {
options = {
...options,
layer: features["layer"] ?? options.layer
}
const root = new TiledFeatureSource(0, 0, 0, null, options)
features.features?.addCallbackAndRunD(feats => root.addFeatures(feats))
return root;
@ -200,6 +204,6 @@ export interface TiledFeatureSourceOptions {
* Setting 'dontEnforceMinZoomLevel' will still allow bigger zoom levels for those features
*/
readonly dontEnforceMinZoom?: boolean,
readonly registerTile?: (tile: TiledFeatureSource & Tiled) => void,
readonly registerTile?: (tile: TiledFeatureSource & FeatureSourceForLayer & Tiled) => void,
readonly layer?: FilteredLayer
}

View file

@ -25,8 +25,7 @@ export class GeoOperations {
}
static centerpointCoordinates(feature: any): [number, number] {
// @ts-ignore
return turf.center(feature).geometry.coordinates;
return <[number, number]> turf.center(feature).geometry.coordinates;
}
/**
@ -35,7 +34,7 @@ export class GeoOperations {
* @param lonlat1
*/
static distanceBetween(lonlat0: [number, number], lonlat1: [number, number]) {
return turf.distance(lonlat0, lonlat1) * 1000
return turf.distance(lonlat0, lonlat1, {units: "meters"})
}
/**
@ -196,8 +195,8 @@ export class GeoOperations {
static buffer(feature: any, bufferSizeInMeter: number) {
return turf.buffer(feature, bufferSizeInMeter / 1000, {
units: 'kilometers'
})
units:'kilometers'
} )
}
static bbox(feature: any) {

View file

@ -1,4 +1,4 @@
import SimpleMetaTagger from "./SimpleMetaTagger";
import SimpleMetaTaggers from "./SimpleMetaTagger";
import {ExtraFuncParams, ExtraFunctions} from "./ExtraFunctions";
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
import State from "../State";
@ -33,9 +33,8 @@ export default class MetaTagging {
return;
}
const metatagsToApply: SimpleMetaTagger [] = []
for (const metatag of SimpleMetaTagger.metatags) {
const metatagsToApply: SimpleMetaTaggers[] = []
for (const metatag of SimpleMetaTaggers.metatags) {
if (metatag.includesDates) {
if (options.includeDates ?? true) {
metatagsToApply.push(metatag)
@ -59,19 +58,23 @@ export default class MetaTagging {
let somethingChanged = false
for (const metatag of metatagsToApply) {
try {
// @ts-ignore
if (!metatag.keys.some(key => feature.properties[key] === undefined)) {
// All keys are already defined, we probably already ran this one
continue
}
// @ts-ignore
if (metatag.isLazy) {
somethingChanged = true;
// @ts-ignore
metatag.applyMetaTagsOnFeature(feature, freshness, layer)
} else {
// @ts-ignore
const newValueAdded = metatag.applyMetaTagsOnFeature(feature, freshness, layer)
/* Note that the expression:
* `somethingChanged = newValueAdded || metatag.applyMetaTagsOnFeature(feature, freshness)`
@ -83,6 +86,7 @@ export default class MetaTagging {
somethingChanged = newValueAdded || somethingChanged
}
} catch (e) {
// @ts-ignore
console.error("Could not calculate metatag for ", metatag.keys.join(","), ":", e, e.stack)
}
}
@ -117,8 +121,9 @@ export default class MetaTagging {
const func = new Function("feat", "return " + code + ";");
const f = (feature: any) => {
delete feature.properties[key]
Object.defineProperty(feature.properties, key, {
configurable: true,
enumerable: false, // By setting this as not enumerable, the localTileSaver will _not_ calculate this
@ -149,7 +154,6 @@ export default class MetaTagging {
}
})
}

View file

@ -7,18 +7,92 @@ import BaseUIElement from "../UI/BaseUIElement";
import Title from "../UI/Base/Title";
import {FixedUiElement} from "../UI/Base/FixedUiElement";
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
import {CountryCoder} from "latlon2country"
const cardinalDirections = {
N: 0, NNE: 22.5, NE: 45, ENE: 67.5,
E: 90, ESE: 112.5, SE: 135, SSE: 157.5,
S: 180, SSW: 202.5, SW: 225, WSW: 247.5,
W: 270, WNW: 292.5, NW: 315, NNW: 337.5
export class SimpleMetaTagger {
public readonly keys: string[];
public readonly doc: string;
public readonly isLazy: boolean;
public readonly includesDates: boolean
public readonly applyMetaTagsOnFeature: (feature: any, freshness: Date, layer: LayerConfig) => boolean;
/***
* A function that adds some extra data to a feature
* @param docs: what does this extra data do?
* @param f: apply the changes. Returns true if something changed
*/
constructor(docs: { keys: string[], doc: string, includesDates?: boolean, isLazy?: boolean, cleanupRetagger?: boolean },
f: ((feature: any, freshness: Date, layer: LayerConfig) => boolean)) {
this.keys = docs.keys;
this.doc = docs.doc;
this.isLazy = docs.isLazy
this.applyMetaTagsOnFeature = f;
this.includesDates = docs.includesDates ?? false;
if (!docs.cleanupRetagger) {
for (const key of docs.keys) {
if (!key.startsWith('_') && key.toLowerCase().indexOf("theme") < 0) {
throw `Incorrect metakey ${key}: it should start with underscore (_)`
}
}
}
}
}
export class CountryTagger extends SimpleMetaTagger {
private static readonly coder = new CountryCoder("https://pietervdvn.github.io/latlon2country/");
public runningTasks: Set<any>;
constructor() {
const runningTasks= new Set<any>();
super
(
{
keys: ["_country"],
doc: "The country code of the property (with latlon2country)",
includesDates: false
},
((feature, _) => {
let centerPoint: any = GeoOperations.centerpoint(feature);
const lat = centerPoint.geometry.coordinates[1];
const lon = centerPoint.geometry.coordinates[0];
runningTasks.add(feature)
CountryTagger.coder.GetCountryCodeAsync(lon, lat).then(
countries => {
runningTasks.delete(feature)
try {
const oldCountry = feature.properties["_country"];
feature.properties["_country"] = countries[0].trim().toLowerCase();
if (oldCountry !== feature.properties["_country"]) {
const tagsSource = State.state?.allElements?.getEventSourceById(feature.properties.id);
tagsSource?.ping();
}
} catch (e) {
console.warn(e)
}
}
).catch(_ => {
runningTasks.delete(feature)
})
return false;
})
)
this.runningTasks = runningTasks;
}
}
export default class SimpleMetaTaggers {
private static readonly cardinalDirections = {
N: 0, NNE: 22.5, NE: 45, ENE: 67.5,
E: 90, ESE: 112.5, SE: 135, SSE: 157.5,
S: 180, SSW: 202.5, SW: 225, WSW: 247.5,
W: 270, WNW: 292.5, NW: 315, NNW: 337.5
}
export default class SimpleMetaTagger {
public static coder: any;
public static readonly objectMetaInfo = new SimpleMetaTagger(
{
keys: ["_last_edit:contributor",
@ -92,7 +166,7 @@ export default class SimpleMetaTagger {
return;
}
return SimpleMetaTagger.removeBothTagging(feature.properties)
return SimpleMetaTaggers.removeBothTagging(feature.properties)
})
)
private static surfaceArea = new SimpleMetaTagger(
@ -197,32 +271,7 @@ export default class SimpleMetaTagger {
return true;
})
)
private static country = new SimpleMetaTagger(
{
keys: ["_country"],
doc: "The country code of the property (with latlon2country)",
includesDates: false
},
((feature, _) => {
let centerPoint: any = GeoOperations.centerpoint(feature);
const lat = centerPoint.geometry.coordinates[1];
const lon = centerPoint.geometry.coordinates[0];
SimpleMetaTagger.coder?.GetCountryCodeFor(lon, lat, (countries: string[]) => {
try {
const oldCountry = feature.properties["_country"];
feature.properties["_country"] = countries[0].trim().toLowerCase();
if (oldCountry !== feature.properties["_country"]) {
const tagsSource = State.state.allElements.getEventSourceById(feature.properties.id);
tagsSource.ping();
}
} catch (e) {
console.warn(e)
}
})
return false;
})
)
public static country = new CountryTagger()
private static isOpen = new SimpleMetaTagger(
{
keys: ["_isOpen", "_isOpen:description"],
@ -319,7 +368,7 @@ export default class SimpleMetaTagger {
if (direction === undefined) {
return false;
}
const n = cardinalDirections[direction] ?? Number(direction);
const n = SimpleMetaTaggers.cardinalDirections[direction] ?? Number(direction);
if (isNaN(n)) {
return false;
}
@ -333,6 +382,7 @@ export default class SimpleMetaTagger {
})
)
private static currentTime = new SimpleMetaTagger(
{
keys: ["_now:date", "_now:datetime", "_loaded:date", "_loaded:_datetime"],
@ -361,49 +411,24 @@ export default class SimpleMetaTagger {
return true;
}
)
public static metatags = [
SimpleMetaTagger.latlon,
SimpleMetaTagger.layerInfo,
SimpleMetaTagger.surfaceArea,
SimpleMetaTagger.lngth,
SimpleMetaTagger.canonicalize,
SimpleMetaTagger.country,
SimpleMetaTagger.isOpen,
SimpleMetaTagger.directionSimplified,
SimpleMetaTagger.currentTime,
SimpleMetaTagger.objectMetaInfo,
SimpleMetaTagger.noBothButLeftRight
public static metatags: SimpleMetaTagger[] = [
SimpleMetaTaggers.latlon,
SimpleMetaTaggers.layerInfo,
SimpleMetaTaggers.surfaceArea,
SimpleMetaTaggers.lngth,
SimpleMetaTaggers.canonicalize,
SimpleMetaTaggers.country,
SimpleMetaTaggers.isOpen,
SimpleMetaTaggers.directionSimplified,
SimpleMetaTaggers.currentTime,
SimpleMetaTaggers.objectMetaInfo,
SimpleMetaTaggers.noBothButLeftRight
];
public readonly keys: string[];
public readonly doc: string;
public readonly isLazy: boolean;
public readonly includesDates: boolean
public readonly applyMetaTagsOnFeature: (feature: any, freshness: Date, layer: LayerConfig) => boolean;
public static readonly lazyTags: string[] = [].concat(...SimpleMetaTagger.metatags.filter(tagger => tagger.isLazy)
public static readonly lazyTags: string[] = [].concat(...SimpleMetaTaggers.metatags.filter(tagger => tagger.isLazy)
.map(tagger => tagger.keys));
/***
* A function that adds some extra data to a feature
* @param docs: what does this extra data do?
* @param f: apply the changes. Returns true if something changed
*/
constructor(docs: { keys: string[], doc: string, includesDates?: boolean, isLazy?: boolean, cleanupRetagger?: boolean },
f: ((feature: any, freshness: Date, layer: LayerConfig) => boolean)) {
this.keys = docs.keys;
this.doc = docs.doc;
this.isLazy = docs.isLazy
this.applyMetaTagsOnFeature = f;
this.includesDates = docs.includesDates ?? false;
if (!docs.cleanupRetagger) {
for (const key of docs.keys) {
if (!key.startsWith('_') && key.toLowerCase().indexOf("theme") < 0) {
throw `Incorrect metakey ${key}: it should start with underscore (_)`
}
}
}
}
/**
* Edits the given object to rewrite 'both'-tagging into a 'left-right' tagging scheme.
@ -494,7 +519,7 @@ export default class SimpleMetaTagger {
subElements.push(new Title("Metatags calculated by MapComplete", 2))
subElements.push(new FixedUiElement("The following values are always calculated, by default, by MapComplete and are available automatically on all elements in every theme"))
for (const metatag of SimpleMetaTagger.metatags) {
for (const metatag of SimpleMetaTaggers.metatags) {
subElements.push(
new Title(metatag.keys.join(", "), 3),
metatag.doc,