Full code cleanup
This commit is contained in:
parent
3a4a2a2016
commit
fa971ffbbf
300 changed files with 16352 additions and 19284 deletions
|
@ -64,7 +64,7 @@ export default class AvailableBaseLayersImplementation implements AvailableBaseL
|
|||
console.warn("Editor layer index: name not defined on ", props)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
|
||||
const leafletLayer: () => TileLayer = () => AvailableBaseLayersImplementation.CreateBackgroundLayer(
|
||||
props.id,
|
||||
|
@ -189,13 +189,13 @@ export default class AvailableBaseLayersImplementation implements AvailableBaseL
|
|||
attribution: attribution,
|
||||
maxZoom: Math.max(21, maxZoom ?? 19),
|
||||
maxNativeZoom: maxZoom ?? 19,
|
||||
minZoom: 1,
|
||||
minZoom: 1,
|
||||
// @ts-ignore
|
||||
wmts: isWMTS ?? false,
|
||||
subdomains: domains
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public AvailableLayersAt(location: UIEventSource<Loc>): UIEventSource<BaseLayer[]> {
|
||||
return UIEventSource.ListStabilized(location.map(
|
||||
(currentLocation) => {
|
||||
|
@ -209,50 +209,50 @@ export default class AvailableBaseLayersImplementation implements AvailableBaseL
|
|||
public SelectBestLayerAccordingTo(location: UIEventSource<Loc>, preferedCategory: UIEventSource<string | string[]>): UIEventSource<BaseLayer> {
|
||||
return this.AvailableLayersAt(location)
|
||||
.map(available => {
|
||||
// First float all 'best layers' to the top
|
||||
available.sort((a, b) => {
|
||||
if (a.isBest && b.isBest) {
|
||||
return 0;
|
||||
}
|
||||
if (!a.isBest) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
)
|
||||
|
||||
if (preferedCategory.data === undefined) {
|
||||
return available[0]
|
||||
}
|
||||
|
||||
let prefered: string []
|
||||
if (typeof preferedCategory.data === "string") {
|
||||
prefered = [preferedCategory.data]
|
||||
} else {
|
||||
prefered = preferedCategory.data;
|
||||
}
|
||||
|
||||
prefered.reverse();
|
||||
for (const category of prefered) {
|
||||
//Then sort all 'photo'-layers to the top. Stability of the sorting will force a 'best' photo layer on top
|
||||
// First float all 'best layers' to the top
|
||||
available.sort((a, b) => {
|
||||
if (a.category === category && b.category === category) {
|
||||
if (a.isBest && b.isBest) {
|
||||
return 0;
|
||||
}
|
||||
if (a.category !== category) {
|
||||
if (!a.isBest) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
)
|
||||
}
|
||||
return available[0]
|
||||
}, [preferedCategory])
|
||||
|
||||
if (preferedCategory.data === undefined) {
|
||||
return available[0]
|
||||
}
|
||||
|
||||
let prefered: string []
|
||||
if (typeof preferedCategory.data === "string") {
|
||||
prefered = [preferedCategory.data]
|
||||
} else {
|
||||
prefered = preferedCategory.data;
|
||||
}
|
||||
|
||||
prefered.reverse();
|
||||
for (const category of prefered) {
|
||||
//Then sort all 'photo'-layers to the top. Stability of the sorting will force a 'best' photo layer on top
|
||||
available.sort((a, b) => {
|
||||
if (a.category === category && b.category === category) {
|
||||
return 0;
|
||||
}
|
||||
if (a.category !== category) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
)
|
||||
}
|
||||
return available[0]
|
||||
}, [preferedCategory])
|
||||
}
|
||||
|
||||
|
||||
|
||||
private CalculateAvailableLayersAt(lon: number, lat: number): BaseLayer[] {
|
||||
const availableLayers = [this.osmCarto]
|
||||
const globalLayers = [];
|
||||
|
|
|
@ -11,11 +11,11 @@ export interface GeoLocationPointProperties {
|
|||
"user:location": "yes",
|
||||
"date": string,
|
||||
"latitude": number
|
||||
"longitude":number,
|
||||
"longitude": number,
|
||||
"speed": number,
|
||||
"accuracy": number
|
||||
"heading": number
|
||||
"altitude":number
|
||||
"altitude": number
|
||||
}
|
||||
|
||||
export default class GeoLocationHandler extends VariableUiElement {
|
||||
|
|
|
@ -64,22 +64,6 @@ export default class DetermineLayout {
|
|||
return layoutToUse
|
||||
}
|
||||
|
||||
private static prepCustomTheme(json: any): LayoutConfigJson{
|
||||
const knownLayersDict = new Map<string, LayerConfigJson>()
|
||||
for (const key in known_layers["default"]) {
|
||||
const layer = known_layers["default"][key]
|
||||
knownLayersDict.set(layer.id, layer)
|
||||
}
|
||||
const converState = {
|
||||
tagRenderings: SharedTagRenderings.SharedTagRenderingJson,
|
||||
sharedLayers: knownLayersDict
|
||||
}
|
||||
json = new FixLegacyTheme().convertStrict(converState, json, "While loading a dynamic theme")
|
||||
json = new PrepareTheme().convertStrict(converState, json, "While preparing a dynamic theme")
|
||||
console.log("The layoutconfig is ", json)
|
||||
return json
|
||||
}
|
||||
|
||||
public static LoadLayoutFromHash(
|
||||
userLayoutParam: UIEventSource<string>
|
||||
): LayoutConfig | null {
|
||||
|
@ -148,6 +132,22 @@ export default class DetermineLayout {
|
|||
.AttachTo("centermessage");
|
||||
}
|
||||
|
||||
private static prepCustomTheme(json: any): LayoutConfigJson {
|
||||
const knownLayersDict = new Map<string, LayerConfigJson>()
|
||||
for (const key in known_layers["default"]) {
|
||||
const layer = known_layers["default"][key]
|
||||
knownLayersDict.set(layer.id, layer)
|
||||
}
|
||||
const converState = {
|
||||
tagRenderings: SharedTagRenderings.SharedTagRenderingJson,
|
||||
sharedLayers: knownLayersDict
|
||||
}
|
||||
json = new FixLegacyTheme().convertStrict(converState, json, "While loading a dynamic theme")
|
||||
json = new PrepareTheme().convertStrict(converState, json, "While preparing a dynamic theme")
|
||||
console.log("The layoutconfig is ", json)
|
||||
return json
|
||||
}
|
||||
|
||||
private static async LoadRemoteTheme(link: string): Promise<LayoutConfig | null> {
|
||||
console.log("Downloading map theme from ", link);
|
||||
|
||||
|
@ -160,7 +160,7 @@ export default class DetermineLayout {
|
|||
try {
|
||||
parsed.id = link;
|
||||
const layoutToUse = DetermineLayout.prepCustomTheme(parsed)
|
||||
return new LayoutConfig(layoutToUse,false)
|
||||
return new LayoutConfig(layoutToUse, false)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
DetermineLayout.ShowErrorOnCustomTheme(
|
||||
|
|
|
@ -94,7 +94,7 @@ class IntersectionFunc implements ExtraFunction {
|
|||
for (const otherFeature of tile) {
|
||||
|
||||
const intersections = GeoOperations.LineIntersections(feat, otherFeature)
|
||||
if(intersections.length === 0){
|
||||
if (intersections.length === 0) {
|
||||
continue
|
||||
}
|
||||
result.push({feat: otherFeature, intersections})
|
||||
|
@ -154,28 +154,12 @@ class ClosestObjectFunc implements ExtraFunction {
|
|||
|
||||
|
||||
class ClosestNObjectFunc implements ExtraFunction {
|
||||
_f(params, feature) {
|
||||
|
||||
return (features, amount, uniqueTag, maxDistanceInMeters) => {
|
||||
let distance: number = Number(maxDistanceInMeters)
|
||||
if (isNaN(distance)) {
|
||||
distance = undefined
|
||||
}
|
||||
return ClosestNObjectFunc.GetClosestNFeatures(params, feature, features, {
|
||||
maxFeatures: Number(amount),
|
||||
uniqueTag: uniqueTag,
|
||||
maxDistance: distance
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_name = "closestn"
|
||||
_doc = "Given either a list of geojson features or a single layer name, gives the n closest objects which are nearest to the feature (excluding the feature itself). In the case of ways/polygons, only the centerpoint is considered. " +
|
||||
"Returns a list of `{feat: geojson, distance:number}` the empty list if nothing is found (or not yet loaded)\n\n" +
|
||||
"If a 'unique tag key' is given, the tag with this key will only appear once (e.g. if 'name' is given, all features will have a different name)"
|
||||
_args = ["list of features or layer name or '*' to get all features", "amount of features", "unique tag key (optional)", "maxDistanceInMeters (optional)"]
|
||||
|
||||
|
||||
/**
|
||||
* Gets the closes N features, sorted by ascending distance.
|
||||
*
|
||||
|
@ -311,6 +295,21 @@ class ClosestNObjectFunc implements ExtraFunction {
|
|||
return closestFeatures;
|
||||
}
|
||||
|
||||
_f(params, feature) {
|
||||
|
||||
return (features, amount, uniqueTag, maxDistanceInMeters) => {
|
||||
let distance: number = Number(maxDistanceInMeters)
|
||||
if (isNaN(distance)) {
|
||||
distance = undefined
|
||||
}
|
||||
return ClosestNObjectFunc.GetClosestNFeatures(params, feature, features, {
|
||||
maxFeatures: Number(amount),
|
||||
uniqueTag: uniqueTag,
|
||||
maxDistance: distance
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -401,7 +400,7 @@ export class ExtraFunctions {
|
|||
];
|
||||
|
||||
public static FullPatchFeature(params: ExtraFuncParams, feature) {
|
||||
if(feature._is_patched){
|
||||
if (feature._is_patched) {
|
||||
return
|
||||
}
|
||||
feature._is_patched = true
|
||||
|
|
|
@ -14,7 +14,7 @@ class MetatagUpdater {
|
|||
private source: FeatureSourceForLayer & Tiled;
|
||||
private readonly params: ExtraFuncParams
|
||||
private state: { allElements?: ElementStorage };
|
||||
|
||||
|
||||
private readonly isDirty = new UIEventSource(false)
|
||||
|
||||
constructor(source: FeatureSourceForLayer & Tiled, state: { allElements?: ElementStorage }, featurePipeline: FeaturePipeline) {
|
||||
|
@ -31,14 +31,14 @@ class MetatagUpdater {
|
|||
if (oldBbox === undefined) {
|
||||
self.neededLayerBboxes.set(layerId, bbox);
|
||||
} else if (!bbox.isContainedIn(oldBbox)) {
|
||||
self.neededLayerBboxes.set(layerId,oldBbox.unionWith(bbox))
|
||||
self.neededLayerBboxes.set(layerId, oldBbox.unionWith(bbox))
|
||||
}
|
||||
return featurePipeline.GetFeaturesWithin(layerId, bbox)
|
||||
},
|
||||
memberships: featurePipeline.relationTracker
|
||||
}
|
||||
this.isDirty.stabilized(100).addCallback(dirty => {
|
||||
if(dirty){
|
||||
if (dirty) {
|
||||
self.updateMetaTags()
|
||||
}
|
||||
})
|
||||
|
@ -46,10 +46,10 @@ class MetatagUpdater {
|
|||
|
||||
}
|
||||
|
||||
public requestUpdate(){
|
||||
public requestUpdate() {
|
||||
this.isDirty.setData(true)
|
||||
}
|
||||
|
||||
|
||||
private updateMetaTags() {
|
||||
const features = this.source.features.data
|
||||
|
||||
|
@ -74,7 +74,8 @@ export default class MetaTagRecalculator {
|
|||
};
|
||||
private _featurePipeline: FeaturePipeline;
|
||||
private readonly _alreadyRegistered: Set<FeatureSourceForLayer & Tiled> = new Set<FeatureSourceForLayer & Tiled>()
|
||||
private readonly _notifiers : MetatagUpdater[] = []
|
||||
private readonly _notifiers: MetatagUpdater[] = []
|
||||
|
||||
/**
|
||||
* The meta tag recalculator receives tiles of layers.
|
||||
* It keeps track of which sources have had their share calculated, and which should be re-updated if some other data is loaded
|
||||
|
@ -92,16 +93,16 @@ private readonly _notifiers : MetatagUpdater[] = []
|
|||
return;
|
||||
}
|
||||
this._alreadyRegistered.add(source)
|
||||
this._notifiers.push(new MetatagUpdater(source,this._state,this._featurePipeline))
|
||||
this._notifiers.push(new MetatagUpdater(source, this._state, this._featurePipeline))
|
||||
const self = this;
|
||||
source.features.addCallbackAndRunD(_ => {
|
||||
const layerName = source.layer.layerDef.id
|
||||
for (const updater of self._notifiers ) {
|
||||
for (const updater of self._notifiers) {
|
||||
const neededBbox = updater.neededLayerBboxes.get(layerName)
|
||||
if(neededBbox == undefined){
|
||||
if (neededBbox == undefined) {
|
||||
continue
|
||||
}
|
||||
if(source.bbox === undefined || neededBbox.overlapsWith(source.bbox)){
|
||||
if (source.bbox === undefined || neededBbox.overlapsWith(source.bbox)) {
|
||||
updater.requestUpdate()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ export default class SaveTileToLocalStorageActor {
|
|||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
public LoadTilesFromDisk(currentBounds: UIEventSource<BBox>, location: UIEventSource<Loc>,
|
||||
registerFreshness: (tileId: number, freshness: Date) => void,
|
||||
registerTile: ((src: FeatureSource & Tiled) => void)) {
|
||||
|
@ -55,7 +55,7 @@ export default class SaveTileToLocalStorageActor {
|
|||
}
|
||||
currentBounds.addCallbackAndRunD(bbox => {
|
||||
|
||||
if(self._layer.minzoomVisible > location.data.zoom){
|
||||
if (self._layer.minzoomVisible > location.data.zoom) {
|
||||
// Not enough zoom
|
||||
return;
|
||||
}
|
||||
|
@ -119,9 +119,9 @@ export default class SaveTileToLocalStorageActor {
|
|||
}
|
||||
|
||||
private SetIdb(tileIndex, data) {
|
||||
try{
|
||||
try {
|
||||
IdbLocalStorage.SetDirectly(this._layer.id + "_" + tileIndex, data)
|
||||
}catch(e){
|
||||
} catch (e) {
|
||||
console.error("Could not save tile to indexed-db: ", e, "tileIndex is:", tileIndex, "for layer", this._layer.id)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,40 +43,33 @@ export default class FeaturePipeline {
|
|||
public readonly timeout: UIEventSource<number>;
|
||||
public readonly somethingLoaded: UIEventSource<boolean> = new UIEventSource<boolean>(false)
|
||||
public readonly newDataLoadedSignal: UIEventSource<FeatureSource> = new UIEventSource<FeatureSource>(undefined)
|
||||
|
||||
|
||||
|
||||
public readonly relationTracker: RelationsTracker
|
||||
/**
|
||||
* Keeps track of all raw OSM-nodes.
|
||||
* Only initialized if 'type_node' is defined as layer
|
||||
*/
|
||||
public readonly fullNodeDatabase?: FullNodeDatabaseSource
|
||||
private readonly overpassUpdater: OverpassFeatureSource
|
||||
private state: MapState;
|
||||
public readonly relationTracker: RelationsTracker
|
||||
private readonly perLayerHierarchy: Map<string, TileHierarchyMerger>;
|
||||
|
||||
/**
|
||||
* Keeps track of the age of the loaded data.
|
||||
* Has one freshness-Calculator for every layer
|
||||
* @private
|
||||
*/
|
||||
private readonly freshnesses = new Map<string, TileFreshnessCalculator>();
|
||||
|
||||
private readonly oldestAllowedDate: Date;
|
||||
private readonly osmSourceZoomLevel
|
||||
|
||||
private readonly localStorageSavers = new Map<string, SaveTileToLocalStorageActor>()
|
||||
|
||||
/**
|
||||
* Keeps track of all raw OSM-nodes.
|
||||
* Only initialized if 'type_node' is defined as layer
|
||||
*/
|
||||
public readonly fullNodeDatabase? : FullNodeDatabaseSource
|
||||
|
||||
|
||||
constructor(
|
||||
handleFeatureSource: (source: FeatureSourceForLayer & Tiled) => void,
|
||||
state: MapState,
|
||||
options? : {
|
||||
options?: {
|
||||
/*Used for metatagging - will receive all the sources with changeGeometry applied but without filtering*/
|
||||
handleRawFeatureSource: (source: FeatureSourceForLayer) => void
|
||||
}
|
||||
) {
|
||||
) {
|
||||
this.state = state;
|
||||
|
||||
const self = this
|
||||
|
@ -104,7 +97,7 @@ export default class FeaturePipeline {
|
|||
return location.zoom >= minzoom;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
const neededTilesFromOsm = this.getNeededTilesFromOsm(this.sufficientlyZoomed)
|
||||
|
||||
const perLayerHierarchy = new Map<string, TileHierarchyMerger>()
|
||||
|
@ -114,10 +107,10 @@ export default class FeaturePipeline {
|
|||
function patchedHandleFeatureSource(src: FeatureSourceForLayer & IndexedFeatureSource & Tiled) {
|
||||
// This will already contain the merged features for this tile. In other words, this will only be triggered once for every tile
|
||||
const withChanges = new ChangeGeometryApplicator(src, state.changes);
|
||||
const srcFiltered = new FilteringFeatureSource(state, src.tileIndex,withChanges)
|
||||
const srcFiltered = new FilteringFeatureSource(state, src.tileIndex, withChanges)
|
||||
|
||||
handleFeatureSource(srcFiltered)
|
||||
if(options?.handleRawFeatureSource){
|
||||
if (options?.handleRawFeatureSource) {
|
||||
options.handleRawFeatureSource(withChanges)
|
||||
}
|
||||
self.somethingLoaded.setData(true)
|
||||
|
@ -267,7 +260,7 @@ export default class FeaturePipeline {
|
|||
})
|
||||
})
|
||||
|
||||
if(this.fullNodeDatabase !== undefined){
|
||||
if (this.fullNodeDatabase !== undefined) {
|
||||
osmFeatureSource.rawDataHandlers.push((osmJson, tileId) => this.fullNodeDatabase.handleOsmJson(osmJson, tileId))
|
||||
}
|
||||
|
||||
|
@ -289,7 +282,7 @@ export default class FeaturePipeline {
|
|||
self.localStorageSavers.get(tile.layer.layerDef.id)?.addTile(tile)
|
||||
perLayerHierarchy.get(source.layer.layerDef.id).registerTile(new RememberingSource(tile))
|
||||
tile.features.addCallbackAndRunD(f => {
|
||||
if(f.length === 0){
|
||||
if (f.length === 0) {
|
||||
return
|
||||
}
|
||||
self.onNewDataLoaded(tile)
|
||||
|
@ -298,9 +291,11 @@ export default class FeaturePipeline {
|
|||
}
|
||||
}),
|
||||
updater,
|
||||
{handleLeftovers: (leftOvers) => {
|
||||
console.warn("Overpass returned a few non-matched features:", leftOvers)
|
||||
}})
|
||||
{
|
||||
handleLeftovers: (leftOvers) => {
|
||||
console.warn("Overpass returned a few non-matched features:", leftOvers)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// Also load points/lines that are newly added.
|
||||
|
@ -308,7 +303,7 @@ export default class FeaturePipeline {
|
|||
newGeometry.features.addCallbackAndRun(geometries => {
|
||||
console.debug("New geometries are:", geometries)
|
||||
})
|
||||
|
||||
|
||||
new RegisteringAllFromFeatureSourceActor(newGeometry, state.allElements)
|
||||
// A NewGeometryFromChangesFeatureSource does not split per layer, so we do this next
|
||||
new PerLayerFeatureSourceSplitter(state.filteredLayers,
|
||||
|
@ -322,9 +317,11 @@ export default class FeaturePipeline {
|
|||
|
||||
},
|
||||
newGeometry,
|
||||
{handleLeftovers: (leftOvers) => {
|
||||
console.warn("Got some leftovers from the filteredLayers: ", leftOvers)
|
||||
}}
|
||||
{
|
||||
handleLeftovers: (leftOvers) => {
|
||||
console.warn("Got some leftovers from the filteredLayers: ", leftOvers)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
this.runningQuery = updater.runningQuery.map(
|
||||
|
@ -337,10 +334,6 @@ export default class FeaturePipeline {
|
|||
|
||||
}
|
||||
|
||||
private onNewDataLoaded(src: FeatureSource){
|
||||
this.newDataLoadedSignal.setData(src)
|
||||
}
|
||||
|
||||
public GetAllFeaturesWithin(bbox: BBox): any[][] {
|
||||
const self = this
|
||||
const tiles = []
|
||||
|
@ -369,6 +362,10 @@ export default class FeaturePipeline {
|
|||
})
|
||||
}
|
||||
|
||||
private onNewDataLoaded(src: FeatureSource) {
|
||||
this.newDataLoadedSignal.setData(src)
|
||||
}
|
||||
|
||||
private freshnessForVisibleLayers(z: number, x: number, y: number): Date {
|
||||
let oldestDate = undefined;
|
||||
for (const flayer of this.state.filteredLayers.data) {
|
||||
|
@ -378,11 +375,11 @@ export default class FeaturePipeline {
|
|||
if (this.state.locationControl.data.zoom < flayer.layerDef.minzoom) {
|
||||
continue;
|
||||
}
|
||||
if(flayer.layerDef.maxAgeOfCache === 0){
|
||||
if (flayer.layerDef.maxAgeOfCache === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const freshnessCalc = this.freshnesses.get(flayer.layerDef.id)
|
||||
if(freshnessCalc === undefined){
|
||||
if (freshnessCalc === undefined) {
|
||||
console.warn("No freshness tracker found for ", flayer.layerDef.id)
|
||||
return undefined
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
|||
};
|
||||
private readonly _alreadyRegistered = new Set<UIEventSource<any>>();
|
||||
private readonly _is_dirty = new UIEventSource(false)
|
||||
private previousFeatureSet : Set<any> = undefined;
|
||||
|
||||
private previousFeatureSet: Set<any> = undefined;
|
||||
|
||||
constructor(
|
||||
state: {
|
||||
locationControl: UIEventSource<{ zoom: number }>,
|
||||
|
@ -54,7 +54,7 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
|||
self.update()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
metataggingUpdated?.addCallback(_ => {
|
||||
self._is_dirty.setData(true)
|
||||
})
|
||||
|
@ -66,7 +66,7 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
|||
const self = this;
|
||||
const layer = this.upstream.layer;
|
||||
const features: { feature: any; freshness: Date }[] = (this.upstream.features.data ?? []);
|
||||
const includedFeatureIds = new Set<string>();
|
||||
const includedFeatureIds = new Set<string>();
|
||||
const newFeatures = (features ?? []).filter((f) => {
|
||||
|
||||
self.registerCallback(f.feature)
|
||||
|
@ -97,29 +97,29 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
|||
|
||||
const previousSet = this.previousFeatureSet;
|
||||
this._is_dirty.setData(false)
|
||||
|
||||
|
||||
// Is there any difference between the two sets?
|
||||
if(previousSet !== undefined && previousSet.size === includedFeatureIds.size){
|
||||
if (previousSet !== undefined && previousSet.size === includedFeatureIds.size) {
|
||||
// The size of the sets is the same - they _might_ be identical
|
||||
const newItemFound = Array.from(includedFeatureIds).some(id => !previousSet.has(id))
|
||||
if(!newItemFound){
|
||||
if (!newItemFound) {
|
||||
// We know that:
|
||||
// - The sets have the same size
|
||||
// - Every item from the new set has been found in the old set
|
||||
// which means they are identical!
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Something new has been found!
|
||||
this.features.setData(newFeatures);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private registerCallback(feature: any) {
|
||||
const src = this.state?.allElements?.addOrGetElement(feature)
|
||||
if(src == undefined){
|
||||
if (src == undefined) {
|
||||
return
|
||||
}
|
||||
if (this._alreadyRegistered.has(src)) {
|
||||
|
|
|
@ -65,7 +65,7 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource {
|
|||
for (const kv of change.tags) {
|
||||
feat.tags[kv.k] = kv.v
|
||||
}
|
||||
const geojson= feat.asGeoJson();
|
||||
const geojson = feat.asGeoJson();
|
||||
allElementStorage.addOrGetElement(geojson)
|
||||
self.features.data.push({feature: geojson, freshness: new Date()})
|
||||
self.features.ping()
|
||||
|
@ -81,7 +81,7 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource {
|
|||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
try {
|
||||
const tags = {}
|
||||
for (const kv of change.tags) {
|
||||
|
|
|
@ -10,7 +10,7 @@ export default class SimpleFeatureSource implements FeatureSourceForLayer, Tiled
|
|||
public readonly bbox: BBox = BBox.global;
|
||||
public readonly tileIndex: number;
|
||||
|
||||
constructor(layer: FilteredLayer, tileIndex: number, featureSource?: UIEventSource<{ feature:any; freshness: Date }[]>) {
|
||||
constructor(layer: FilteredLayer, tileIndex: number, featureSource?: UIEventSource<{ feature: any; freshness: Date }[]>) {
|
||||
this.name = "SimpleFeatureSource(" + layer.layerDef.id + ")"
|
||||
this.layer = layer
|
||||
this.tileIndex = tileIndex ?? 0;
|
||||
|
|
|
@ -40,7 +40,7 @@ export default class DynamicGeoJsonTileSource extends DynamicTileSource {
|
|||
json => {
|
||||
const data = new Map<number, Set<number>>();
|
||||
for (const x in json) {
|
||||
if(x === "zoom"){
|
||||
if (x === "zoom") {
|
||||
continue
|
||||
}
|
||||
data.set(Number(x), new Set(json[x]))
|
||||
|
@ -91,7 +91,7 @@ export default class DynamicGeoJsonTileSource extends DynamicTileSource {
|
|||
public static RegisterWhitelist(url: string, json: any) {
|
||||
const data = new Map<number, Set<number>>();
|
||||
for (const x in json) {
|
||||
if(x === "zoom"){
|
||||
if (x === "zoom") {
|
||||
continue
|
||||
}
|
||||
data.set(Number(x), new Set(json[x]))
|
||||
|
|
|
@ -44,11 +44,11 @@ export default class DynamicTileSource implements TileHierarchy<FeatureSourceFor
|
|||
return undefined
|
||||
}
|
||||
const tileRange = Tiles.TileRangeBetween(zoomlevel, bounds.getNorth(), bounds.getEast(), bounds.getSouth(), bounds.getWest())
|
||||
if(tileRange.total > 10000){
|
||||
if (tileRange.total > 10000) {
|
||||
console.error("Got a really big tilerange, bounds and location might be out of sync")
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
||||
const needed = Tiles.MapRange(tileRange, (x, y) => Tiles.tile_index(zoomlevel, x, y)).filter(i => !self._loadedTiles.has(i))
|
||||
if (needed.length === 0) {
|
||||
return undefined
|
||||
|
|
|
@ -45,8 +45,8 @@ export default class FullNodeDatabaseSource implements TileHierarchy<FeatureSour
|
|||
for (const nodeId of osmWay.nodes) {
|
||||
|
||||
if (!this.parentWays.has(nodeId)) {
|
||||
const src = new UIEventSource<OsmWay[]>([])
|
||||
this.parentWays.set(nodeId,src)
|
||||
const src = new UIEventSource<OsmWay[]>([])
|
||||
this.parentWays.set(nodeId, src)
|
||||
src.addCallback(parentWays => {
|
||||
const tgs = nodesById.get(nodeId).tags
|
||||
tgs ["parent_ways"] = JSON.stringify(parentWays.map(w => w.tags))
|
||||
|
|
|
@ -89,8 +89,8 @@ export default class OsmFeatureSource {
|
|||
if (z > 20) {
|
||||
throw "This is an absurd high zoom level"
|
||||
}
|
||||
|
||||
if( z < 14){
|
||||
|
||||
if (z < 14) {
|
||||
throw `Zoom ${z} is too much for OSM to handle! Use a higher zoom level!`
|
||||
}
|
||||
|
||||
|
|
|
@ -558,19 +558,19 @@ export class GeoOperations {
|
|||
const prevCoordinate = coordinates[i - 1]
|
||||
|
||||
const distP = GeoOperations.distanceBetween(coordinate, prevCoordinate)
|
||||
if(distP < 0.1){
|
||||
if (distP < 0.1) {
|
||||
coordinates.splice(i, 1)
|
||||
continue
|
||||
}
|
||||
|
||||
if(i == coordinates.length - 2){
|
||||
|
||||
if (i == coordinates.length - 2) {
|
||||
const distN = GeoOperations.distanceBetween(coordinate, nextCoordinate)
|
||||
if(distN < 0.1){
|
||||
if (distN < 0.1) {
|
||||
coordinates.splice(i, 1)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const bearingN = turf.bearing(coordinate, nextCoordinate)
|
||||
const bearingP = turf.bearing(prevCoordinate, coordinate)
|
||||
const diff = Math.abs(bearingN - bearingP)
|
||||
|
@ -683,8 +683,8 @@ export class GeoOperations {
|
|||
throw "CalculateIntersection fallthrough: can not calculate an intersection between features"
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ export default class MetaTagging {
|
|||
|
||||
private static errorPrintCount = 0;
|
||||
private static readonly stopErrorOutputAt = 10;
|
||||
private static retaggingFuncCache = new Map<string, ((feature: any) => void)[]>()
|
||||
|
||||
/**
|
||||
* This method (re)calculates all metatags and calculated tags on every given object.
|
||||
|
@ -24,7 +25,7 @@ export default class MetaTagging {
|
|||
public static addMetatags(features: { feature: any; freshness: Date }[],
|
||||
params: ExtraFuncParams,
|
||||
layer: LayerConfig,
|
||||
state?: {allElements?: ElementStorage},
|
||||
state?: { allElements?: ElementStorage },
|
||||
options?: {
|
||||
includeDates?: true | boolean,
|
||||
includeNonDates?: true | boolean
|
||||
|
@ -56,7 +57,7 @@ export default class MetaTagging {
|
|||
const feature = ff.feature
|
||||
const freshness = ff.freshness
|
||||
let somethingChanged = false
|
||||
let definedTags = new Set(Object.getOwnPropertyNames( feature.properties ))
|
||||
let definedTags = new Set(Object.getOwnPropertyNames(feature.properties))
|
||||
for (const metatag of metatagsToApply) {
|
||||
try {
|
||||
if (!metatag.keys.some(key => feature.properties[key] === undefined)) {
|
||||
|
@ -65,7 +66,7 @@ export default class MetaTagging {
|
|||
}
|
||||
|
||||
if (metatag.isLazy) {
|
||||
if(!metatag.keys.some(key => !definedTags.has(key))) {
|
||||
if (!metatag.keys.some(key => !definedTags.has(key))) {
|
||||
// All keys are defined - lets skip!
|
||||
continue
|
||||
}
|
||||
|
@ -104,7 +105,7 @@ export default class MetaTagging {
|
|||
}
|
||||
return atLeastOneFeatureChanged
|
||||
}
|
||||
|
||||
|
||||
private static createFunctionsForFeature(layerId: string, calculatedTags: [string, string, boolean][]): ((feature: any) => void)[] {
|
||||
const functions: ((feature: any) => any)[] = [];
|
||||
for (const entry of calculatedTags) {
|
||||
|
@ -128,7 +129,7 @@ export default class MetaTagging {
|
|||
delete feat.properties[key]
|
||||
feat.properties[key] = result;
|
||||
return result
|
||||
}catch(e){
|
||||
} catch (e) {
|
||||
if (MetaTagging.errorPrintCount < MetaTagging.stopErrorOutputAt) {
|
||||
console.warn("Could not calculate a " + (isStrict ? "strict " : "") + " calculated tag for key " + key + " defined by " + code + " (in layer" + layerId + ") due to \n" + e + "\n. Are you the theme creator? Doublecheck your code. Note that the metatags might not be stable on new features", e, e.stack)
|
||||
MetaTagging.errorPrintCount++;
|
||||
|
@ -138,10 +139,10 @@ export default class MetaTagging {
|
|||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(isStrict){
|
||||
}
|
||||
|
||||
|
||||
if (isStrict) {
|
||||
functions.push(calculateAndAssign)
|
||||
continue
|
||||
}
|
||||
|
@ -166,8 +167,6 @@ export default class MetaTagging {
|
|||
return functions;
|
||||
}
|
||||
|
||||
private static retaggingFuncCache = new Map<string, ((feature: any) => void)[]>()
|
||||
|
||||
/**
|
||||
* Creates the function which adds all the calculated tags to a feature. Called once per layer
|
||||
* @param layer
|
||||
|
@ -182,7 +181,7 @@ export default class MetaTagging {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
let functions :((feature: any) => void)[] = MetaTagging.retaggingFuncCache.get(layer.id);
|
||||
let functions: ((feature: any) => void)[] = MetaTagging.retaggingFuncCache.get(layer.id);
|
||||
if (functions === undefined) {
|
||||
functions = MetaTagging.createFunctionsForFeature(layer.id, calculatedTags)
|
||||
MetaTagging.retaggingFuncCache.set(layer.id, functions)
|
||||
|
|
|
@ -11,7 +11,7 @@ export default class ChangeLocationAction extends OsmChangeAction {
|
|||
theme: string,
|
||||
reason: string
|
||||
}) {
|
||||
super(id,true);
|
||||
super(id, true);
|
||||
if (!id.startsWith("node/")) {
|
||||
throw "Invalid ID: only 'node/number' is accepted"
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ export default class ChangeLocationAction extends OsmChangeAction {
|
|||
this._newLonLat = newLonLat;
|
||||
this._meta = meta;
|
||||
}
|
||||
|
||||
|
||||
protected async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
|
||||
|
||||
const d: ChangeDescription = {
|
||||
|
|
|
@ -19,7 +19,7 @@ export default class ChangeTagAction extends OsmChangeAction {
|
|||
this._currentTags = currentTags;
|
||||
this._meta = meta;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Doublechecks that no stupid values are added
|
||||
*/
|
||||
|
|
|
@ -14,35 +14,36 @@ import {TagUtils} from "../../Tags/TagUtils";
|
|||
* More or less the same as 'CreateNewWay', except that it'll try to reuse already existing points
|
||||
*/
|
||||
export default class CreateMultiPolygonWithPointReuseAction extends OsmCreateAction {
|
||||
private readonly _tags: Tag[];
|
||||
public newElementId: string = undefined;
|
||||
public newElementIdNumber: number = undefined;
|
||||
public newElementIdNumber: number = undefined;
|
||||
private readonly _tags: Tag[];
|
||||
private readonly createOuterWay: CreateWayWithPointReuseAction
|
||||
private readonly createInnerWays : CreateNewWayAction[]
|
||||
private readonly geojsonPreview: any;
|
||||
private readonly createInnerWays: CreateNewWayAction[]
|
||||
private readonly geojsonPreview: any;
|
||||
private readonly theme: string;
|
||||
private readonly changeType: "import" | "create" | string;
|
||||
|
||||
constructor(tags: Tag[],
|
||||
outerRingCoordinates: [number, number][],
|
||||
innerRingsCoordinates: [number, number][][],
|
||||
innerRingsCoordinates: [number, number][][],
|
||||
state: FeaturePipelineState,
|
||||
config: MergePointConfig[],
|
||||
changeType: "import" | "create" | string
|
||||
) {
|
||||
super(null,true);
|
||||
this._tags = [...tags, new Tag("type","multipolygon")];
|
||||
super(null, true);
|
||||
this._tags = [...tags, new Tag("type", "multipolygon")];
|
||||
this.changeType = changeType;
|
||||
this.theme = state.layoutToUse.id
|
||||
this. createOuterWay = new CreateWayWithPointReuseAction([], outerRingCoordinates, state, config)
|
||||
this. createInnerWays = innerRingsCoordinates.map(ringCoordinates =>
|
||||
new CreateNewWayAction([],
|
||||
ringCoordinates.map(([lon, lat] )=> ({lat, lon})),
|
||||
{theme: state.layoutToUse.id}))
|
||||
|
||||
this.geojsonPreview = {
|
||||
this.createOuterWay = new CreateWayWithPointReuseAction([], outerRingCoordinates, state, config)
|
||||
this.createInnerWays = innerRingsCoordinates.map(ringCoordinates =>
|
||||
new CreateNewWayAction([],
|
||||
ringCoordinates.map(([lon, lat]) => ({lat, lon})),
|
||||
{theme: state.layoutToUse.id}))
|
||||
|
||||
this.geojsonPreview = {
|
||||
type: "Feature",
|
||||
properties: TagUtils.changeAsProperties(new And(this._tags).asChange({})),
|
||||
geometry:{
|
||||
geometry: {
|
||||
type: "Polygon",
|
||||
coordinates: [
|
||||
outerRingCoordinates,
|
||||
|
@ -59,7 +60,7 @@ private readonly geojsonPreview: any;
|
|||
freshness: new Date(),
|
||||
feature: this.geojsonPreview
|
||||
})
|
||||
return outerPreview
|
||||
return outerPreview
|
||||
}
|
||||
|
||||
protected async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
|
||||
|
@ -72,14 +73,14 @@ private readonly geojsonPreview: any;
|
|||
|
||||
|
||||
this.newElementIdNumber = changes.getNewID();
|
||||
this.newElementId = "relation/"+this.newElementIdNumber
|
||||
this.newElementId = "relation/" + this.newElementIdNumber
|
||||
descriptions.push({
|
||||
type:"relation",
|
||||
type: "relation",
|
||||
id: this.newElementIdNumber,
|
||||
tags: new And(this._tags).asChange({}),
|
||||
meta: {
|
||||
theme: this.theme,
|
||||
changeType:this.changeType
|
||||
changeType: this.changeType
|
||||
},
|
||||
changes: {
|
||||
members: [
|
||||
|
@ -93,8 +94,8 @@ private readonly geojsonPreview: any;
|
|||
]
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
return descriptions
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ export default class CreateNewNodeAction extends OsmCreateAction {
|
|||
changeType: "create" | "import" | null,
|
||||
specialMotivation?: string
|
||||
}) {
|
||||
super(null,basicTags !== undefined && basicTags.length > 0)
|
||||
super(null, basicTags !== undefined && basicTags.length > 0)
|
||||
this._basicTags = basicTags;
|
||||
this._lat = lat;
|
||||
this._lon = lon;
|
||||
|
@ -46,7 +46,7 @@ export default class CreateNewNodeAction extends OsmCreateAction {
|
|||
this.meta = {
|
||||
theme: options.theme,
|
||||
changeType: options.changeType,
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ export default class CreateNewWayAction extends OsmCreateAction {
|
|||
options: {
|
||||
theme: string
|
||||
}) {
|
||||
super(null,true)
|
||||
super(null, true)
|
||||
this.coordinates = coordinates;
|
||||
this.tags = tags;
|
||||
this._options = options;
|
||||
|
@ -56,7 +56,7 @@ export default class CreateNewWayAction extends OsmCreateAction {
|
|||
|
||||
|
||||
const id = changes.getNewID()
|
||||
this.newElementIdNumber = id
|
||||
this.newElementIdNumber = id
|
||||
const newWay = <ChangeDescription>{
|
||||
id,
|
||||
type: "way",
|
||||
|
|
|
@ -20,14 +20,14 @@ export interface MergePointConfig {
|
|||
|
||||
/**
|
||||
* CreateWayWithPointreuse will create a 'CoordinateInfo' for _every_ point in the way to be created.
|
||||
*
|
||||
*
|
||||
* The CoordinateInfo indicates the action to take, e.g.:
|
||||
*
|
||||
*
|
||||
* - Create a new point
|
||||
* - Reuse an existing OSM point (and don't move it)
|
||||
* - Reuse an existing OSM point (and leave it where it is)
|
||||
* - Reuse another Coordinate info (and don't do anything else with it)
|
||||
*
|
||||
*
|
||||
*/
|
||||
interface CoordinateInfo {
|
||||
/**
|
||||
|
@ -56,6 +56,8 @@ interface CoordinateInfo {
|
|||
* More or less the same as 'CreateNewWay', except that it'll try to reuse already existing points
|
||||
*/
|
||||
export default class CreateWayWithPointReuseAction extends OsmCreateAction {
|
||||
public newElementId: string = undefined;
|
||||
public newElementIdNumber: number = undefined
|
||||
private readonly _tags: Tag[];
|
||||
/**
|
||||
* lngLat-coordinates
|
||||
|
@ -64,20 +66,17 @@ export default class CreateWayWithPointReuseAction extends OsmCreateAction {
|
|||
private _coordinateInfo: CoordinateInfo[];
|
||||
private _state: FeaturePipelineState;
|
||||
private _config: MergePointConfig[];
|
||||
|
||||
public newElementId: string = undefined;
|
||||
public newElementIdNumber: number = undefined
|
||||
|
||||
constructor(tags: Tag[],
|
||||
coordinates: [number, number][],
|
||||
state: FeaturePipelineState,
|
||||
config: MergePointConfig[]
|
||||
) {
|
||||
super(null,true);
|
||||
super(null, true);
|
||||
this._tags = tags;
|
||||
this._state = state;
|
||||
this._config = config;
|
||||
|
||||
|
||||
// The main logic of this class: the coordinateInfo contains all the changes
|
||||
this._coordinateInfo = this.CalculateClosebyNodes(coordinates);
|
||||
|
||||
|
@ -117,7 +116,7 @@ export default class CreateWayWithPointReuseAction extends OsmCreateAction {
|
|||
"move": "yes",
|
||||
"osm-id": reusedPoint.node.properties.id,
|
||||
"id": "new-geometry-move-existing" + i,
|
||||
"distance":GeoOperations.distanceBetween(coordinateInfo.lngLat, reusedPoint.node.geometry.coordinates)
|
||||
"distance": GeoOperations.distanceBetween(coordinateInfo.lngLat, reusedPoint.node.geometry.coordinates)
|
||||
},
|
||||
geometry: {
|
||||
type: "LineString",
|
||||
|
@ -136,7 +135,7 @@ export default class CreateWayWithPointReuseAction extends OsmCreateAction {
|
|||
"move": "no",
|
||||
"osm-id": reusedPoint.node.properties.id,
|
||||
"id": "new-geometry-reuse-existing" + i,
|
||||
"distance":GeoOperations.distanceBetween(coordinateInfo.lngLat, reusedPoint.node.geometry.coordinates)
|
||||
"distance": GeoOperations.distanceBetween(coordinateInfo.lngLat, reusedPoint.node.geometry.coordinates)
|
||||
},
|
||||
geometry: {
|
||||
type: "LineString",
|
||||
|
@ -238,7 +237,7 @@ export default class CreateWayWithPointReuseAction extends OsmCreateAction {
|
|||
const newWay = new CreateNewWayAction(this._tags, nodeIdsToUse, {
|
||||
theme
|
||||
})
|
||||
|
||||
|
||||
allChanges.push(...(await newWay.CreateChangeDescriptions(changes)))
|
||||
this.newElementId = newWay.newElementId
|
||||
this.newElementIdNumber = newWay.newElementIdNumber
|
||||
|
@ -266,7 +265,7 @@ export default class CreateWayWithPointReuseAction extends OsmCreateAction {
|
|||
}[]
|
||||
}[] = coordinates.map(_ => undefined)
|
||||
|
||||
|
||||
|
||||
// First loop: gather all information...
|
||||
for (let i = 0; i < coordinates.length; i++) {
|
||||
|
||||
|
@ -328,7 +327,7 @@ export default class CreateWayWithPointReuseAction extends OsmCreateAction {
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Second loop: figure out which point moves where without creating conflicts
|
||||
let conflictFree = true;
|
||||
do {
|
||||
|
@ -348,8 +347,8 @@ export default class CreateWayWithPointReuseAction extends OsmCreateAction {
|
|||
if (other.closebyNodes === undefined || other.closebyNodes[0] === undefined) {
|
||||
continue
|
||||
}
|
||||
|
||||
if(coorInfo.closebyNodes[0] === undefined){
|
||||
|
||||
if (coorInfo.closebyNodes[0] === undefined) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ export default class DeleteAction extends OsmChangeAction {
|
|||
specialMotivation: string
|
||||
},
|
||||
hardDelete: boolean) {
|
||||
super(id,true)
|
||||
super(id, true)
|
||||
this._id = id;
|
||||
this._hardDelete = hardDelete;
|
||||
this.meta = {...meta, changeType: "deletion"};
|
||||
|
@ -51,7 +51,7 @@ export default class DeleteAction extends OsmChangeAction {
|
|||
return await new ChangeTagAction(
|
||||
this._id, this._softDeletionTags, osmObject.tags,
|
||||
{
|
||||
... this.meta,
|
||||
...this.meta,
|
||||
changeType: "soft-delete"
|
||||
}
|
||||
).CreateChangeDescriptions(changes)
|
||||
|
|
|
@ -7,7 +7,6 @@ import {ChangeDescription} from "./ChangeDescription";
|
|||
|
||||
export default abstract class OsmChangeAction {
|
||||
|
||||
private isUsed = false
|
||||
public readonly trackStatistics: boolean;
|
||||
/**
|
||||
* The ID of the object that is the center of this change.
|
||||
|
@ -15,7 +14,8 @@ export default abstract class OsmChangeAction {
|
|||
* Undefined if such an id does not make sense
|
||||
*/
|
||||
public readonly mainObjectId: string;
|
||||
|
||||
private isUsed = false
|
||||
|
||||
constructor(mainObjectId: string, trackStatistics: boolean = true) {
|
||||
this.trackStatistics = trackStatistics;
|
||||
this.mainObjectId = mainObjectId
|
||||
|
@ -32,9 +32,9 @@ export default abstract class OsmChangeAction {
|
|||
protected abstract CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]>
|
||||
}
|
||||
|
||||
export abstract class OsmCreateAction extends OsmChangeAction{
|
||||
export abstract class OsmCreateAction extends OsmChangeAction {
|
||||
|
||||
public newElementId : string
|
||||
public newElementId: string
|
||||
public newElementIdNumber: number
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -16,10 +16,11 @@ abstract class AbstractRelationSplitHandler extends OsmChangeAction {
|
|||
protected readonly _theme: string;
|
||||
|
||||
constructor(input: RelationSplitInput, theme: string) {
|
||||
super("relation/"+input.relation.id, false)
|
||||
super("relation/" + input.relation.id, false)
|
||||
this._input = input;
|
||||
this._theme = theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns which node should border the member at the given index
|
||||
*/
|
||||
|
|
|
@ -468,12 +468,12 @@ export default class ReplaceGeometryAction extends OsmChangeAction {
|
|||
proj.sort((a, b) => {
|
||||
// Sort descending
|
||||
const diff = b.projectAfterIndex - a.projectAfterIndex;
|
||||
if(diff !== 0){
|
||||
if (diff !== 0) {
|
||||
return diff
|
||||
}
|
||||
return b.distance - a.distance;
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
||||
for (const reprojectedNode of proj) {
|
||||
|
|
|
@ -26,7 +26,7 @@ export default class SplitAction extends OsmChangeAction {
|
|||
* @param toleranceInMeters: if a splitpoint closer then this amount of meters to an existing point, the existing point will be used to split the line instead of a new point
|
||||
*/
|
||||
constructor(wayId: string, splitPointCoordinates: [number, number][], meta: { theme: string }, toleranceInMeters = 5) {
|
||||
super(wayId,true)
|
||||
super(wayId, true)
|
||||
this.wayId = wayId;
|
||||
this._splitPointsCoordinates = splitPointCoordinates
|
||||
this._toleranceInMeters = toleranceInMeters;
|
||||
|
|
|
@ -27,16 +27,13 @@ export class Changes {
|
|||
public features = new UIEventSource<{ feature: any, freshness: Date }[]>([]);
|
||||
public readonly pendingChanges: UIEventSource<ChangeDescription[]> = LocalStorageSource.GetParsed<ChangeDescription[]>("pending-changes", [])
|
||||
public readonly allChanges = new UIEventSource<ChangeDescription[]>(undefined)
|
||||
public readonly state: { allElements: ElementStorage; historicalUserLocations: FeatureSource; osmConnection: OsmConnection }
|
||||
public readonly extraComment: UIEventSource<string> = new UIEventSource(undefined)
|
||||
private _nextId: number = -1; // Newly assigned ID's are negative
|
||||
private readonly isUploading = new UIEventSource(false);
|
||||
|
||||
private readonly previouslyCreated: OsmObject[] = []
|
||||
private readonly _leftRightSensitive: boolean;
|
||||
|
||||
public readonly state: { allElements: ElementStorage; historicalUserLocations: FeatureSource; osmConnection: OsmConnection }
|
||||
|
||||
public readonly extraComment:UIEventSource<string> = new UIEventSource(undefined)
|
||||
|
||||
constructor(
|
||||
state?: {
|
||||
allElements: ElementStorage,
|
||||
|
@ -107,7 +104,7 @@ export class Changes {
|
|||
* Uploads all the pending changes in one go.
|
||||
* Triggered by the 'PendingChangeUploader'-actor in Actors
|
||||
*/
|
||||
public async flushChanges(flushreason: string = undefined, openChangeset?: UIEventSource<number>) : Promise<void>{
|
||||
public async flushChanges(flushreason: string = undefined, openChangeset?: UIEventSource<number>): Promise<void> {
|
||||
if (this.pendingChanges.data.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -116,19 +113,37 @@ export class Changes {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
console.log("Uploading changes due to: ", flushreason)
|
||||
this.isUploading.setData(true)
|
||||
try {
|
||||
const csNumber = await this.flushChangesAsync(openChangeset)
|
||||
this.isUploading.setData(false)
|
||||
console.log("Changes flushed. Your changeset is "+csNumber);
|
||||
console.log("Changes flushed. Your changeset is " + csNumber);
|
||||
} catch (e) {
|
||||
this.isUploading.setData(false)
|
||||
console.error("Flushing changes failed due to", e);
|
||||
}
|
||||
}
|
||||
|
||||
public async applyAction(action: OsmChangeAction): Promise<void> {
|
||||
const changeDescriptions = await action.Perform(this)
|
||||
changeDescriptions[0].meta.distanceToObject = this.calculateDistanceToChanges(action, changeDescriptions)
|
||||
this.applyChanges(changeDescriptions)
|
||||
}
|
||||
|
||||
public applyChanges(changes: ChangeDescription[]) {
|
||||
console.log("Received changes:", changes)
|
||||
this.pendingChanges.data.push(...changes);
|
||||
this.pendingChanges.ping();
|
||||
this.allChanges.data.push(...changes)
|
||||
this.allChanges.ping()
|
||||
}
|
||||
|
||||
public registerIdRewrites(mappings: Map<string, string>): void {
|
||||
CreateNewNodeAction.registerIdRewrites(mappings)
|
||||
}
|
||||
|
||||
private calculateDistanceToChanges(change: OsmChangeAction, changeDescriptions: ChangeDescription[]) {
|
||||
|
||||
const locations = this.state?.historicalUserLocations?.features?.data
|
||||
|
@ -140,7 +155,7 @@ export class Changes {
|
|||
// Probably irrelevant, such as a new helper node
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const now = new Date()
|
||||
const recentLocationPoints = locations.map(ff => ff.feature)
|
||||
.filter(feat => feat.geometry.type === "Point")
|
||||
|
@ -188,26 +203,6 @@ export class Changes {
|
|||
))
|
||||
}
|
||||
|
||||
public async applyAction(action: OsmChangeAction): Promise<void> {
|
||||
const changeDescriptions = await action.Perform(this)
|
||||
changeDescriptions[0].meta.distanceToObject = this.calculateDistanceToChanges(action, changeDescriptions)
|
||||
this.applyChanges(changeDescriptions)
|
||||
}
|
||||
|
||||
public applyChanges(changes: ChangeDescription[]) {
|
||||
console.log("Received changes:", changes)
|
||||
this.pendingChanges.data.push(...changes);
|
||||
this.pendingChanges.ping();
|
||||
this.allChanges.data.push(...changes)
|
||||
this.allChanges.ping()
|
||||
}
|
||||
|
||||
|
||||
public registerIdRewrites(mappings: Map<string, string>): void {
|
||||
CreateNewNodeAction.registerIdRewrites(mappings)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* UPload the selected changes to OSM.
|
||||
* Returns 'true' if successfull and if they can be removed
|
||||
|
@ -287,10 +282,10 @@ export class Changes {
|
|||
// This method is only called with changedescriptions for this theme
|
||||
const theme = pending[0].meta.theme
|
||||
let comment = "Adding data with #MapComplete for theme #" + theme
|
||||
if(this.extraComment.data !== undefined){
|
||||
comment+="\n\n"+this.extraComment.data
|
||||
if (this.extraComment.data !== undefined) {
|
||||
comment += "\n\n" + this.extraComment.data
|
||||
}
|
||||
|
||||
|
||||
const metatags: ChangesetTag[] = [{
|
||||
key: "comment",
|
||||
value: comment
|
||||
|
@ -329,10 +324,10 @@ export class Changes {
|
|||
pendingPerTheme.get(theme).push(changeDescription)
|
||||
}
|
||||
|
||||
const successes = await Promise.all(Array.from(pendingPerTheme,
|
||||
async ([theme, pendingChanges]) => {
|
||||
const successes = await Promise.all(Array.from(pendingPerTheme,
|
||||
async ([theme, pendingChanges]) => {
|
||||
try {
|
||||
if(openChangeset === undefined){
|
||||
if (openChangeset === undefined) {
|
||||
openChangeset = this.state.osmConnection.GetPreference("current-open-changeset-" + theme).map(
|
||||
str => {
|
||||
const n = Number(str);
|
||||
|
@ -342,9 +337,9 @@ export class Changes {
|
|||
return n
|
||||
}, [], n => "" + n
|
||||
);
|
||||
console.log("Using current-open-changeset-"+theme+" from the preferences, got "+openChangeset.data)
|
||||
console.log("Using current-open-changeset-" + theme + " from the preferences, got " + openChangeset.data)
|
||||
}
|
||||
|
||||
|
||||
return await self.flushSelectChanges(pendingChanges, openChangeset);
|
||||
} catch (e) {
|
||||
console.error("Could not upload some changes:", e)
|
||||
|
@ -395,7 +390,7 @@ export class Changes {
|
|||
// Might be a failed fetch for simply this object
|
||||
throw "Did not get an object that should be known: " + id
|
||||
}
|
||||
if(change.changes === undefined){
|
||||
if (change.changes === undefined) {
|
||||
// This object is a change to a newly created object. However, we have not seen the creation changedescription yet!
|
||||
throw "Not a creation of the object"
|
||||
}
|
||||
|
@ -522,7 +517,7 @@ export class Changes {
|
|||
|
||||
})
|
||||
|
||||
console.debug("Calculated the pending changes: ", result.newObjects.length,"new; ", result.modifiedObjects.length,"modified;",result.deletedObjects,"deleted")
|
||||
console.debug("Calculated the pending changes: ", result.newObjects.length, "new; ", result.modifiedObjects.length, "modified;", result.deletedObjects, "deleted")
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -61,7 +61,7 @@ export class ChangesetHandler {
|
|||
if (!extraMetaTags.some(tag => tag.key === "comment") || !extraMetaTags.some(tag => tag.key === "theme")) {
|
||||
throw "The meta tags should at least contain a `comment` and a `theme`"
|
||||
}
|
||||
|
||||
|
||||
if (this.userDetails.data.csCount == 0) {
|
||||
// The user became a contributor!
|
||||
this.userDetails.data.csCount = 1;
|
||||
|
|
|
@ -122,6 +122,34 @@ export class OsmPreferences {
|
|||
return pref;
|
||||
}
|
||||
|
||||
public ClearPreferences() {
|
||||
let isRunning = false;
|
||||
const self = this;
|
||||
this.preferences.addCallbackAndRun(prefs => {
|
||||
if (Object.keys(prefs).length == 0) {
|
||||
return;
|
||||
}
|
||||
if (isRunning) {
|
||||
return
|
||||
}
|
||||
isRunning = true
|
||||
const prefixes = ["mapcomplete-installed-theme", "mapcomplete-installed-themes-", "mapcomplete-current-open-changeset", "mapcomplete-personal-theme-layer"]
|
||||
for (const key in prefs) {
|
||||
for (const prefix of prefixes) {
|
||||
// console.log(key)
|
||||
if (key.startsWith(prefix)) {
|
||||
console.log("Clearing ", key)
|
||||
self.GetPreference(key, "").setData("")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
isRunning = false;
|
||||
return true;
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private UpdatePreferences() {
|
||||
const self = this;
|
||||
this.auth.xhr({
|
||||
|
@ -184,34 +212,6 @@ export class OsmPreferences {
|
|||
console.debug(`Preference ${k} written!`);
|
||||
});
|
||||
}
|
||||
|
||||
public ClearPreferences(){
|
||||
let isRunning = false;
|
||||
const self = this;
|
||||
this.preferences.addCallbackAndRun(prefs => {
|
||||
if(Object.keys(prefs).length == 0){
|
||||
return;
|
||||
}
|
||||
if (isRunning) {
|
||||
return
|
||||
}
|
||||
isRunning = true
|
||||
const prefixes = ["mapcomplete-installed-theme","mapcomplete-installed-themes-","mapcomplete-current-open-changeset","mapcomplete-personal-theme-layer"]
|
||||
for (const key in prefs) {
|
||||
for (const prefix of prefixes) {
|
||||
// console.log(key)
|
||||
if (key.startsWith(prefix)) {
|
||||
console.log("Clearing ", key)
|
||||
self.GetPreference(key, "").setData("")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
isRunning = false;
|
||||
return true;
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -125,7 +125,8 @@ export default class SimpleMetaTaggers {
|
|||
return changed
|
||||
}
|
||||
)
|
||||
|
||||
public static readonly lazyTags: string[] = [].concat(...SimpleMetaTaggers.metatags.filter(tagger => tagger.isLazy)
|
||||
.map(tagger => tagger.keys));
|
||||
private static readonly cardinalDirections = {
|
||||
N: 0, NNE: 22.5, NE: 45, ENE: 67.5,
|
||||
E: 90, ESE: 112.5, SE: 135, SSE: 157.5,
|
||||
|
@ -290,27 +291,27 @@ export default class SimpleMetaTaggers {
|
|||
// isOpen is irrelevant
|
||||
return false
|
||||
}
|
||||
if(feature.properties.opening_hours === "24/7"){
|
||||
if (feature.properties.opening_hours === "24/7") {
|
||||
feature.properties._isOpen = "yes"
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Object.defineProperty(feature.properties, "_isOpen", {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
get: () => {
|
||||
if(feature.properties.id === "node/7464543832"){
|
||||
console.log("Getting _isOpen for ", feature.properties.i)
|
||||
if (feature.properties.id === "node/7464543832") {
|
||||
console.log("Getting _isOpen for ", feature.properties.i)
|
||||
}
|
||||
delete feature.properties._isOpen
|
||||
feature.properties._isOpen = undefined
|
||||
const tagsSource = state.allElements.getEventSourceById(feature.properties.id);
|
||||
tagsSource.addCallbackAndRunD(tags => {
|
||||
// Install a listener to the tags...
|
||||
if (tags.opening_hours === undefined){
|
||||
if (tags.opening_hours === undefined) {
|
||||
return;
|
||||
}
|
||||
if(tags._country === undefined) {
|
||||
if (tags._country === undefined) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
@ -322,7 +323,7 @@ export default class SimpleMetaTaggers {
|
|||
country_code: tags._country.toLowerCase()
|
||||
}
|
||||
}, {tag_key: "opening_hours"});
|
||||
|
||||
|
||||
// AUtomatically triggered on the next change (and a bit below)
|
||||
const updateTags = () => {
|
||||
const oldValueIsOpen = tags["_isOpen"];
|
||||
|
@ -442,8 +443,6 @@ export default class SimpleMetaTaggers {
|
|||
SimpleMetaTaggers.geometryType
|
||||
|
||||
];
|
||||
public static readonly lazyTags: string[] = [].concat(...SimpleMetaTaggers.metatags.filter(tagger => tagger.isLazy)
|
||||
.map(tagger => tagger.keys));
|
||||
|
||||
/**
|
||||
* Edits the given object to rewrite 'both'-tagging into a 'left-right' tagging scheme.
|
||||
|
|
|
@ -49,7 +49,7 @@ export default class ElementsState extends FeatureSwitchState {
|
|||
super(layoutToUse);
|
||||
|
||||
// @ts-ignore
|
||||
this.changes = new Changes(this,layoutToUse?.isLeftRightSensitive() ?? false)
|
||||
this.changes = new Changes(this, layoutToUse?.isLeftRightSensitive() ?? false)
|
||||
{
|
||||
// -- Location control initialization
|
||||
const zoom = UIEventSource.asFloat(
|
||||
|
|
|
@ -20,7 +20,8 @@ export default class FeaturePipelineState extends MapState {
|
|||
*/
|
||||
public readonly featurePipeline: FeaturePipeline;
|
||||
private readonly featureAggregator: TileHierarchyAggregator;
|
||||
private readonly metatagRecalculator : MetaTagRecalculator
|
||||
private readonly metatagRecalculator: MetaTagRecalculator
|
||||
|
||||
constructor(layoutToUse: LayoutConfig) {
|
||||
super(layoutToUse);
|
||||
|
||||
|
@ -33,21 +34,21 @@ private readonly metatagRecalculator : MetaTagRecalculator
|
|||
* We are a bit in a bind:
|
||||
* There is the featurePipeline, which creates some sources during construction
|
||||
* THere is the metatagger, which needs to have these sources registered AND which takes a FeaturePipeline as argument
|
||||
*
|
||||
*
|
||||
* This is a bit of a catch-22 (except that it isn't)
|
||||
* The sources that are registered in the constructor are saved into 'registeredSources' temporary
|
||||
*
|
||||
* The sources that are registered in the constructor are saved into 'registeredSources' temporary
|
||||
*
|
||||
*/
|
||||
const sourcesToRegister = []
|
||||
|
||||
function registerRaw(source: FeatureSourceForLayer & Tiled){
|
||||
if(self.metatagRecalculator === undefined){
|
||||
|
||||
function registerRaw(source: FeatureSourceForLayer & Tiled) {
|
||||
if (self.metatagRecalculator === undefined) {
|
||||
sourcesToRegister.push(source)
|
||||
}else{
|
||||
} else {
|
||||
self.metatagRecalculator.registerSource(source)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function registerSource(source: FeatureSourceForLayer & Tiled) {
|
||||
|
||||
clusterCounter.addTile(source)
|
||||
|
@ -127,7 +128,7 @@ private readonly metatagRecalculator : MetaTagRecalculator
|
|||
this.metatagRecalculator.registerSource(this.currentView, true)
|
||||
|
||||
sourcesToRegister.forEach(source => self.metatagRecalculator.registerSource(source))
|
||||
|
||||
|
||||
new SelectedFeatureHandler(Hash.hash, this)
|
||||
|
||||
this.AddClusteringToMap(this.leafletMap)
|
||||
|
|
|
@ -223,7 +223,7 @@ export default class MapState extends UserRelatedState {
|
|||
private initGpsLocation() {
|
||||
// Initialize the gps layer data. This is emtpy for now, the actual writing happens in the Geolocationhandler
|
||||
let gpsLayerDef: FilteredLayer = this.filteredLayers.data.filter(l => l.layerDef.id === "gps_location")[0]
|
||||
if(gpsLayerDef === undefined){
|
||||
if (gpsLayerDef === undefined) {
|
||||
return
|
||||
}
|
||||
this.currentUserLocation = new SimpleFeatureSource(gpsLayerDef, Tiles.tile_index(0, 0, 0));
|
||||
|
@ -269,7 +269,7 @@ export default class MapState extends UserRelatedState {
|
|||
|
||||
|
||||
let gpsLayerDef: FilteredLayer = this.filteredLayers.data.filter(l => l.layerDef.id === "gps_location_history")[0]
|
||||
if(gpsLayerDef !== undefined){
|
||||
if (gpsLayerDef !== undefined) {
|
||||
this.historicalUserLocations = new SimpleFeatureSource(gpsLayerDef, Tiles.tile_index(0, 0, 0), features);
|
||||
}
|
||||
|
||||
|
@ -299,7 +299,7 @@ export default class MapState extends UserRelatedState {
|
|||
}]
|
||||
})
|
||||
let gpsLineLayerDef: FilteredLayer = this.filteredLayers.data.filter(l => l.layerDef.id === "gps_track")[0]
|
||||
if(gpsLineLayerDef !== undefined){
|
||||
if (gpsLineLayerDef !== undefined) {
|
||||
this.historicalUserLocationsTrack = new SimpleFeatureSource(gpsLineLayerDef, Tiles.tile_index(0, 0, 0), asLine);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,14 +96,14 @@ export default class UserRelatedState extends ElementsState {
|
|||
// We wait till we are logged in
|
||||
return
|
||||
}
|
||||
|
||||
if(self.osmConnection.isLoggedIn.data == false){
|
||||
|
||||
if (self.osmConnection.isLoggedIn.data == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentThemes.some(installed => installed.id === this.layoutToUse.id)) {
|
||||
// Already added to the 'installed theme' list
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Current installed themes are", this.installedThemes.data)
|
||||
|
@ -115,13 +115,11 @@ export default class UserRelatedState extends ElementsState {
|
|||
})
|
||||
self.installedThemes.ping()
|
||||
console.log("Registered " + self.layoutToUse.id + " as installed themes")
|
||||
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Important: the favourite layers are initialized _after_ the installed themes, as these might contain an installedTheme
|
||||
this.favouriteLayers = LocalStorageSource.Get("favouriteLayers")
|
||||
.syncWith(this.osmConnection.GetLongPreference("favouriteLayers"))
|
||||
|
|
|
@ -117,7 +117,7 @@ export class And extends TagsFilter {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
AsJson() {
|
||||
return {
|
||||
and: this.and.map(a => a.AsJson())
|
||||
|
|
|
@ -38,7 +38,7 @@ export default class ComparingTag implements TagsFilter {
|
|||
usedKeys(): string[] {
|
||||
return [this._key];
|
||||
}
|
||||
|
||||
|
||||
AsJson() {
|
||||
return this.asHumanString(false, false, {})
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ export class Or extends TagsFilter {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
AsJson() {
|
||||
return {
|
||||
or: this.or.map(o => o.AsJson())
|
||||
|
|
|
@ -46,13 +46,13 @@ export class Tag extends TagsFilter {
|
|||
if (shorten) {
|
||||
v = Utils.EllipsesAfter(v, 25);
|
||||
}
|
||||
if(v === "" || v === undefined){
|
||||
if (v === "" || v === undefined) {
|
||||
// This tag will be removed if in the properties, so we indicate this with special rendering
|
||||
if(currentProperties !== undefined && (currentProperties[this.key] ?? "") === ""){
|
||||
if (currentProperties !== undefined && (currentProperties[this.key] ?? "") === "") {
|
||||
// This tag is not present in the current properties, so this tag doesn't change anything
|
||||
return ""
|
||||
}
|
||||
return "<span class='line-through'>"+this.key+"</span>"
|
||||
return "<span class='line-through'>" + this.key + "</span>"
|
||||
}
|
||||
if (linkToWiki) {
|
||||
return `<a href='https://wiki.openstreetmap.org/wiki/Key:${this.key}' target='_blank'>${this.key}</a>` +
|
||||
|
@ -83,7 +83,7 @@ export class Tag extends TagsFilter {
|
|||
asChange(properties: any): { k: string; v: string }[] {
|
||||
return [{k: this.key, v: this.value}];
|
||||
}
|
||||
|
||||
|
||||
AsJson() {
|
||||
return this.asHumanString(false, false)
|
||||
}
|
||||
|
|
|
@ -245,7 +245,7 @@ export class TagUtils {
|
|||
}
|
||||
return new RegexTag(
|
||||
split[0],
|
||||
new RegExp("^" + split[1] + "$"),
|
||||
new RegExp("^" + split[1] + "$"),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ export class UIEventSource<T> {
|
|||
constructor(data: T, tag: string = "") {
|
||||
this.tag = tag;
|
||||
this.data = data;
|
||||
if(tag === undefined || tag === ""){
|
||||
if (tag === undefined || tag === "") {
|
||||
const callstack = new Error().stack.split("\n")
|
||||
this.tag = callstack[1]
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ export class UIEventSource<T> {
|
|||
source.addCallback((latestData) => {
|
||||
sink.setData(latestData?.data);
|
||||
latestData.addCallback(data => {
|
||||
if(source.data !== latestData){
|
||||
if (source.data !== latestData) {
|
||||
return true;
|
||||
}
|
||||
sink.setData(data)
|
||||
|
@ -243,8 +243,8 @@ export class UIEventSource<T> {
|
|||
}
|
||||
}
|
||||
let endTime = new Date().getTime() / 1000
|
||||
if((endTime - startTime) > 500){
|
||||
console.trace("Warning: a ping of ",this.tag," took more then 500ms; this is probably a performance issue")
|
||||
if ((endTime - startTime) > 500) {
|
||||
console.trace("Warning: a ping of ", this.tag, " took more then 500ms; this is probably a performance issue")
|
||||
}
|
||||
if (toDelete !== undefined) {
|
||||
for (const toDeleteElement of toDelete) {
|
||||
|
@ -297,10 +297,10 @@ export class UIEventSource<T> {
|
|||
|
||||
const stack = new Error().stack.split("\n");
|
||||
const callee = stack[1]
|
||||
|
||||
|
||||
const newSource = new UIEventSource<J>(
|
||||
f(this.data),
|
||||
"map(" + this.tag + ")@"+callee
|
||||
"map(" + this.tag + ")@" + callee
|
||||
);
|
||||
|
||||
const update = function () {
|
||||
|
|
|
@ -7,24 +7,24 @@ import {Utils} from "../../Utils";
|
|||
*/
|
||||
export class IdbLocalStorage {
|
||||
|
||||
|
||||
public static Get<T>(key: string, options?: { defaultValue?: T , whenLoaded?: (t: T) => void}): UIEventSource<T>{
|
||||
const src = new UIEventSource<T>(options?.defaultValue, "idb-local-storage:"+key)
|
||||
if(Utils.runningFromConsole){
|
||||
|
||||
public static Get<T>(key: string, options?: { defaultValue?: T, whenLoaded?: (t: T) => void }): UIEventSource<T> {
|
||||
const src = new UIEventSource<T>(options?.defaultValue, "idb-local-storage:" + key)
|
||||
if (Utils.runningFromConsole) {
|
||||
return src;
|
||||
}
|
||||
idb.get(key).then(v => {
|
||||
src.setData(v ?? options?.defaultValue);
|
||||
if(options?.whenLoaded !== undefined){
|
||||
if (options?.whenLoaded !== undefined) {
|
||||
options?.whenLoaded(v)
|
||||
}
|
||||
})
|
||||
src.addCallback(v => idb.set(key, v))
|
||||
return src;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static SetDirectly(key: string, value){
|
||||
|
||||
public static SetDirectly(key: string, value) {
|
||||
idb.set(key, value)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,15 +7,12 @@ import {Utils} from "../../Utils";
|
|||
|
||||
export class QueryParameters {
|
||||
|
||||
static defaults = {}
|
||||
static documentation = {}
|
||||
private static order: string [] = ["layout", "test", "z", "lat", "lon"];
|
||||
private static _wasInitialized: Set<string> = new Set()
|
||||
private static knownSources = {};
|
||||
private static initialized = false;
|
||||
static defaults = {}
|
||||
|
||||
static documentation = {}
|
||||
|
||||
|
||||
|
||||
public static GetQueryParameter(key: string, deflt: string, documentation?: string): UIEventSource<string> {
|
||||
if (!this.initialized) {
|
||||
|
@ -40,7 +37,6 @@ export class QueryParameters {
|
|||
}
|
||||
|
||||
|
||||
|
||||
public static wasInitialized(key: string): boolean {
|
||||
return QueryParameters._wasInitialized.has(key)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue