Refactoring of GPS-location (uses featureSource too now), factoring out state, add ReplaceGeometryAction and conflation example

This commit is contained in:
pietervdvn 2021-11-03 00:44:53 +01:00
parent 1db54f3c8e
commit 2484848cd6
37 changed files with 1035 additions and 467 deletions

View file

@ -1,13 +1,16 @@
import * as L from "leaflet";
import {UIEventSource} from "../UIEventSource";
import Svg from "../../Svg";
import Img from "../../UI/Base/Img";
import {LocalStorageSource} from "../Web/LocalStorageSource";
import {VariableUiElement} from "../../UI/Base/VariableUIElement";
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
import {QueryParameters} from "../Web/QueryParameters";
import FeatureSource from "../FeatureSource/FeatureSource";
import StaticFeatureSource from "../FeatureSource/Sources/StaticFeatureSource";
export default class GeoLocationHandler extends VariableUiElement {
public readonly currentLocation : FeatureSource
/**
* Wether or not the geolocation is active, aka the user requested the current location
* @private
@ -25,20 +28,12 @@ export default class GeoLocationHandler extends VariableUiElement {
* @private
*/
private readonly _permission: UIEventSource<string>;
/***
* The marker on the map, in order to update it
* @private
*/
private _marker: L.Marker;
/**
* Literally: _currentGPSLocation.data != undefined
* @private
*/
private readonly _hasLocation: UIEventSource<boolean>;
private readonly _currentGPSLocation: UIEventSource<{
latlng: any;
accuracy: number;
}>;
private readonly _currentGPSLocation: UIEventSource<Coordinates>;
/**
* Kept in order to update the marker
* @private
@ -63,8 +58,8 @@ export default class GeoLocationHandler extends VariableUiElement {
private readonly _layoutToUse: LayoutConfig;
constructor(
currentGPSLocation: UIEventSource<{ latlng: any; accuracy: number }>,
leafletMap: UIEventSource<L.Map>,
currentGPSLocation: UIEventSource<Coordinates>,
leafletMap: UIEventSource<any>,
layoutToUse: LayoutConfig
) {
const hasLocation = currentGPSLocation.map(
@ -182,10 +177,25 @@ export default class GeoLocationHandler extends VariableUiElement {
}
})
this.currentLocation = new StaticFeatureSource([], false)
this._currentGPSLocation.addCallback((location) => {
self._previousLocationGrant.setData("granted");
const feature = {
"type": "Feature",
properties: {
"user:location":"yes",
"accuracy":location.accuracy,
"speed":location.speed,
},
geometry:{
type:"Point",
coordinates: [location.longitude, location.latitude],
}
}
self.currentLocation.features.setData([{feature, freshness: new Date()}])
const timeSinceRequest =
(new Date().getTime() - (self._lastUserRequest?.getTime() ?? 0)) / 1000;
if (timeSinceRequest < 30) {
@ -194,33 +204,8 @@ export default class GeoLocationHandler extends VariableUiElement {
self.MoveToCurrentLoction();
}
let color = "#1111cc";
try {
color = getComputedStyle(document.body).getPropertyValue(
"--catch-detail-color"
);
} catch (e) {
console.error(e);
}
const icon = L.icon({
iconUrl: Img.AsData(Svg.location.replace(/#000000/g, color).replace(/#000/g, color)),
iconSize: [40, 40], // size of the icon
iconAnchor: [20, 20], // point of the icon which will correspond to marker's location
});
const map = self._leafletMap.data;
if(map === undefined){
return;
}
const newMarker = L.marker(location.latlng, {icon: icon});
newMarker.addTo(map);
if (self._marker !== undefined) {
map.removeLayer(self._marker);
}
self._marker = newMarker;
});
}
private init(askPermission: boolean, forceZoom: boolean) {
@ -261,8 +246,8 @@ export default class GeoLocationHandler extends VariableUiElement {
this._lastUserRequest = undefined;
if (
this._currentGPSLocation.data.latlng[0] === 0 &&
this._currentGPSLocation.data.latlng[1] === 0
this._currentGPSLocation.data.latitude === 0 &&
this._currentGPSLocation.data.longitude === 0
) {
console.debug("Not moving to GPS-location: it is null island");
return;
@ -275,20 +260,20 @@ export default class GeoLocationHandler extends VariableUiElement {
if (b !== true) {
// B is an array with our locklocation
inRange =
b[0][0] <= location.latlng[0] &&
location.latlng[0] <= b[1][0] &&
b[0][1] <= location.latlng[1] &&
location.latlng[1] <= b[1][1];
b[0][0] <= location.latitude &&
location.latitude <= b[1][0] &&
b[0][1] <= location.longitude &&
location.longitude <= b[1][1];
}
}
if (!inRange) {
console.log(
"Not zooming to GPS location: out of bounds",
b,
location.latlng
location
);
} else {
this._leafletMap.data.setView(location.latlng, targetZoom);
this._leafletMap.data.setView([location.latitude, location.longitude], targetZoom);
}
}
@ -312,10 +297,7 @@ export default class GeoLocationHandler extends VariableUiElement {
navigator.geolocation.watchPosition(
function (position) {
self._currentGPSLocation.setData({
latlng: [position.coords.latitude, position.coords.longitude],
accuracy: position.coords.accuracy,
});
self._currentGPSLocation.setData(position.coords);
},
function () {
console.warn("Could not get location with navigator.geolocation");

View file

@ -116,6 +116,11 @@ export class BBox {
getSouth() {
return this.minLat
}
contains(lonLat: [number, number]){
return this.minLat <= lonLat[1] && lonLat[1] <= this.maxLat
&& this.minLon<= lonLat[0] && lonLat[0] <= this.maxLon
}
pad(factor: number, maxIncrease = 2): BBox {

View file

@ -228,11 +228,15 @@ export default class FeaturePipeline {
})
if(state.layoutToUse.trackAllNodes){
new FullNodeDatabaseSource(state, osmFeatureSource, tile => {
const fullNodeDb = new FullNodeDatabaseSource(
state.filteredLayers.data.filter(l => l.layerDef.id === "type_node")[0],
tile => {
new RegisteringAllFromFeatureSourceActor(tile)
perLayerHierarchy.get(tile.layer.layerDef.id).registerTile(tile)
tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile))
})
osmFeatureSource.rawDataHandlers.push((osmJson, tileId) => fullNodeDb.handleOsmJson(osmJson, tileId))
}

View file

@ -70,7 +70,7 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource {
const w = new OsmWay(change.id)
w.tags = tags
w.nodes = change.changes["nodes"]
w.coordinates = change.changes["coordinates"].map(coor => coor.reverse())
w.coordinates = change.changes["coordinates"].map(coor => [coor[1], coor[0]])
add(w.asGeoJson())
break;
case "relation":

View file

@ -32,7 +32,7 @@ export default class RenderingMultiPlexerFeatureSource {
const withIndex: (any & { pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined })[] = [];
function addAsPoint(feat, rendering, coordinate) {
function addAsPoint(feat, rendering, coordinate) {
const patched = {
...feat,
pointRenderingIndex: rendering.index
@ -46,8 +46,6 @@ export default class RenderingMultiPlexerFeatureSource {
for (const f of features) {
const feat = f.feature;
if (feat.geometry.type === "Point") {
for (const rendering of pointRenderings) {

View file

@ -2,30 +2,103 @@ import TileHierarchy from "./TileHierarchy";
import FeatureSource, {FeatureSourceForLayer, Tiled} from "../FeatureSource";
import {OsmNode, OsmObject, OsmWay} from "../../Osm/OsmObject";
import SimpleFeatureSource from "../Sources/SimpleFeatureSource";
import {UIEventSource} from "../../UIEventSource";
import FilteredLayer from "../../../Models/FilteredLayer";
import {TagsFilter} from "../../Tags/TagsFilter";
import OsmChangeAction from "../../Osm/Actions/OsmChangeAction";
import StaticFeatureSource from "../Sources/StaticFeatureSource";
import {OsmConnection} from "../../Osm/OsmConnection";
import {GeoOperations} from "../../GeoOperations";
import {Utils} from "../../../Utils";
import {UIEventSource} from "../../UIEventSource";
import {BBox} from "../../BBox";
import FeaturePipeline from "../FeaturePipeline";
import {Tag} from "../../Tags/Tag";
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig";
import {ChangeDescription} from "../../Osm/Actions/ChangeDescription";
import CreateNewNodeAction from "../../Osm/Actions/CreateNewNodeAction";
import ChangeTagAction from "../../Osm/Actions/ChangeTagAction";
import {And} from "../../Tags/And";
export default class FullNodeDatabaseSource implements TileHierarchy<FeatureSource & Tiled> {
public readonly loadedTiles = new Map<number, FeatureSource & Tiled>()
private readonly onTileLoaded: (tile: (Tiled & FeatureSourceForLayer)) => void;
private readonly layer : FilteredLayer
private readonly layer: FilteredLayer
constructor(
state: {
readonly filteredLayers: UIEventSource<FilteredLayer[]>},
osmFeatureSource: { rawDataHandlers: ((data: any, tileId: number) => void)[] },
layer: FilteredLayer,
onTileLoaded: ((tile: Tiled & FeatureSourceForLayer) => void)) {
this.onTileLoaded = onTileLoaded
this.layer = state.filteredLayers.data.filter(l => l.layerDef.id === "type_node")[0]
if(this.layer === undefined){
throw "Weird: tracking all nodes, but layer 'type_node' is not defined"
this.layer = layer;
if (this.layer === undefined) {
throw "Layer is undefined"
}
const self = this
osmFeatureSource.rawDataHandlers.push((osmJson, tileId) => self.handleOsmXml(osmJson, tileId))
}
private handleOsmXml(osmJson: any, tileId: number) {
/**
* Given a list of coordinates, will search already existing OSM-points to snap onto.
* Either the geometry will be moved OR the existing point will be moved, depending on configuration and tags.
* This requires the 'type_node'-layer to be activated
*/
public static MergePoints(
state: {
filteredLayers: UIEventSource<FilteredLayer[]>,
featurePipeline: FeaturePipeline,
layoutToUse: LayoutConfig
},
newGeometryLngLats: [number, number][],
configs: ConflationConfig[],
) {
const typeNode = state.filteredLayers.data.filter(l => l.layerDef.id === "type_node")[0]
if (typeNode === undefined) {
throw "Type Node layer is not defined. Add 'type_node' as layer to your layerconfig to use this feature"
}
const bbox = new BBox(newGeometryLngLats)
const bbox_padded = bbox.pad(1.2)
const allNodes: any[] = [].concat(...state.featurePipeline.GetFeaturesWithin("type_node", bbox).map(tile => tile.filter(
feature => bbox_padded.contains(GeoOperations.centerpointCoordinates(feature))
)))
// The strategy: for every point of the new geometry, we search a point that is closeby and matches
// If multiple options match, we choose the most optimal (aka closest)
const maxDistance = Math.max(...configs.map(c => c.withinRangeOfM))
for (const coordinate of newGeometryLngLats) {
let closestNode = undefined;
let closestNodeDistance = undefined
for (const node of allNodes) {
const d = GeoOperations.distanceBetween(GeoOperations.centerpointCoordinates(node), coordinate)
if (d > maxDistance) {
continue
}
let matchesSomeConfig = false
for (const config of configs) {
if (d > config.withinRangeOfM) {
continue
}
if (!config.ifMatches.matchesProperties(node.properties)) {
continue
}
matchesSomeConfig = true;
}
if (!matchesSomeConfig) {
continue
}
if (closestNode === undefined || closestNodeDistance > d) {
closestNode = node;
closestNodeDistance = d;
}
}
}
}
public handleOsmJson(osmJson: any, tileId: number) {
const allObjects = OsmObject.ParseObjects(osmJson.elements)
const nodesById = new Map<number, OsmNode>()
@ -57,7 +130,7 @@ export default class FullNodeDatabaseSource implements TileHierarchy<FeatureSour
})
const now = new Date()
const asGeojsonFeatures = Array.from(nodesById.values()).map(osmNode => ({
feature: osmNode.asGeoJson(),freshness: now
feature: osmNode.asGeoJson(), freshness: now
}))
const featureSource = new SimpleFeatureSource(this.layer, tileId)
@ -66,5 +139,12 @@ export default class FullNodeDatabaseSource implements TileHierarchy<FeatureSour
this.onTileLoaded(featureSource)
}
}
export interface ConflationConfig {
withinRangeOfM: number,
ifMatches: TagsFilter,
mode: "reuse_osm_point" | "move_osm_point"
}

View file

@ -68,7 +68,7 @@ export default class OsmFeatureSource {
console.log("Tile download", Tiles.tile_from_index(neededTile).join("/"), "started")
self.downloadedTiles.add(neededTile)
self.LoadTile(...Tiles.tile_from_index(neededTile)).then(_ => {
console.log("Tile ", Tiles.tile_from_index(neededTile).join("/"), "loaded")
console.debug("Tile ", Tiles.tile_from_index(neededTile).join("/"), "loaded")
})
}
} catch (e) {
@ -98,7 +98,7 @@ export default class OsmFeatureSource {
console.log("Attempting to get tile", z, x, y, "from the osm api")
const osmJson = await Utils.downloadJson(url)
try {
console.log("Got tile", z, x, y, "from the osm api")
console.debug("Got tile", z, x, y, "from the osm api")
this.rawDataHandlers.forEach(handler => handler(osmJson, Tiles.tile_index(z, x, y)))
const geojson = OsmToGeoJson.default(osmJson,
// @ts-ignore
@ -110,10 +110,8 @@ export default class OsmFeatureSource {
// We only keep what is needed
geojson.features = geojson.features.filter(feature => this.allowedTags.matchesProperties(feature.properties))
geojson.features.forEach(f => f.properties["_backend"] = this._backend)
console.log("Tile geojson:", z, x, y, "is", geojson)
const index = Tiles.tile_index(z, x, y);
new PerLayerFeatureSourceSplitter(this.filteredLayers,
this.handleTile,

View file

@ -11,7 +11,7 @@ export default class ChangeTagAction extends OsmChangeAction {
constructor(elementId: string, tagsFilter: TagsFilter, currentTags: any, meta: {
theme: string,
changeType: "answer" | "soft-delete" | "add-image"
changeType: "answer" | "soft-delete" | "add-image" | string
}) {
super();
this._elementId = elementId;
@ -27,11 +27,16 @@ export default class ChangeTagAction extends OsmChangeAction {
const key = kv.k;
const value = kv.v;
if (key === undefined || key === null) {
console.log("Invalid key");
console.error("Invalid key:", key);
return undefined;
}
if (value === undefined || value === null) {
console.log("Invalid value for ", key);
console.error("Invalid value for ", key,":", value);
return undefined;
}
if(typeof value !== "string"){
console.error("Invalid value for ", key, "as it is not a string:", value)
return undefined;
}

View file

@ -4,39 +4,25 @@ import {Changes} from "../Changes";
import {Tag} from "../../Tags/Tag";
import CreateNewNodeAction from "./CreateNewNodeAction";
import {And} from "../../Tags/And";
import {TagsFilter} from "../../Tags/TagsFilter";
export default class CreateNewWayAction extends OsmChangeAction {
public newElementId: string = undefined
private readonly coordinates: ({ nodeId?: number, lat: number, lon: number })[];
private readonly tags: Tag[];
private readonly _options: {
theme: string, existingPointHandling?: {
withinRangeOfM: number,
ifMatches?: TagsFilter,
mode: "reuse_osm_point" | "move_osm_point"
} []
theme: string
};
/***
* Creates a new way to upload to OSM
* @param tags: the tags to apply to the wya
* @param tags: the tags to apply to the way
* @param coordinates: the coordinates. Might have a nodeId, in this case, this node will be used
* @param options
*/
constructor(tags: Tag[], coordinates: ({ nodeId?: number, lat: number, lon: number })[],
options: {
theme: string,
/**
* IF specified, an existing OSM-point within this range and satisfying the condition 'ifMatches' will be used instead of a new coordinate.
* If multiple points are possible, only the closest point is considered
*/
existingPointHandling?: {
withinRangeOfM: number,
ifMatches?: TagsFilter,
mode: "reuse_osm_point" | "move_osm_point"
} []
theme: string
}) {
super()
this.coordinates = coordinates;

View file

@ -0,0 +1,232 @@
import OsmChangeAction from "./OsmChangeAction";
import {Changes} from "../Changes";
import {ChangeDescription} from "./ChangeDescription";
import {Tag} from "../../Tags/Tag";
import FeatureSource from "../../FeatureSource/FeatureSource";
import {OsmNode, OsmObject, OsmWay} from "../OsmObject";
import {GeoOperations} from "../../GeoOperations";
import StaticFeatureSource from "../../FeatureSource/Sources/StaticFeatureSource";
import CreateNewNodeAction from "./CreateNewNodeAction";
import ChangeTagAction from "./ChangeTagAction";
import {And} from "../../Tags/And";
import {Utils} from "../../../Utils";
import {OsmConnection} from "../OsmConnection";
export default class ReplaceGeometryAction extends OsmChangeAction {
private readonly feature: any;
private readonly state: {
osmConnection: OsmConnection
};
private readonly wayToReplaceId: string;
private readonly theme: string;
private readonly targetCoordinates: [number, number][];
private readonly newTags: Tag[] | undefined;
constructor(
state: {
osmConnection: OsmConnection
},
feature: any,
wayToReplaceId: string,
options: {
theme: string,
newTags?: Tag[]
}
) {
super();
this.state = state;
this.feature = feature;
this.wayToReplaceId = wayToReplaceId;
this.theme = options.theme;
const geom = this.feature.geometry
let coordinates: [number, number][]
if (geom.type === "LineString") {
coordinates = geom.coordinates
} else if (geom.type === "Polygon") {
coordinates = geom.coordinates[0]
}
this.targetCoordinates = coordinates
this.newTags = options.newTags
}
public async GetPreview(): Promise<FeatureSource> {
const {closestIds, allNodesById} = await this.GetClosestIds();
const preview = closestIds.map((newId, i) => {
if (newId === undefined) {
return {
type: "Feature",
properties: {
"newpoint": "yes",
"id": "replace-geometry-move-" + i
},
geometry: {
type: "Point",
coordinates: this.targetCoordinates[i]
}
};
}
const origPoint = allNodesById.get(newId).centerpoint()
return {
type: "Feature",
properties: {
"move": "yes",
"osm-id": newId,
"id": "replace-geometry-move-" + i
},
geometry: {
type: "LineString",
coordinates: [[origPoint[1], origPoint[0]], this.targetCoordinates[i]]
}
};
})
return new StaticFeatureSource(preview, false)
}
protected async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
const allChanges: ChangeDescription[] = []
const actualIdsToUse: number[] = []
const {closestIds, osmWay} = await this.GetClosestIds()
for (let i = 0; i < closestIds.length; i++) {
const closestId = closestIds[i];
const [lon, lat] = this.targetCoordinates[i]
if (closestId === undefined) {
const newNodeAction = new CreateNewNodeAction(
[],
lat, lon,
{
allowReuseOfPreviouslyCreatedPoints: true,
theme: this.theme, changeType: null
})
const changeDescr = await newNodeAction.CreateChangeDescriptions(changes)
allChanges.push(...changeDescr)
actualIdsToUse.push(newNodeAction.newElementIdNumber)
} else {
const change = <ChangeDescription>{
id: closestId,
type: "node",
meta: {
theme: this.theme,
changeType: "move"
},
changes: {lon, lat}
}
actualIdsToUse.push(closestId)
allChanges.push(change)
}
}
if (this.newTags !== undefined && this.newTags.length > 0) {
const addExtraTags = new ChangeTagAction(
this.wayToReplaceId,
new And(this.newTags),
osmWay.tags, {
theme: this.theme,
changeType: "conflation"
}
)
allChanges.push(...await addExtraTags.CreateChangeDescriptions(changes))
}
// AT the very last: actually change the nodes of the way!
allChanges.push({
type: "way",
id: osmWay.id,
changes: {
nodes: actualIdsToUse,
coordinates: this.targetCoordinates
},
meta: {
theme: this.theme,
changeType: "conflation"
}
})
return allChanges
}
/**
* For 'this.feature`, gets a corresponding closest node that alreay exsists
* @constructor
* @private
*/
private async GetClosestIds(): Promise<{ closestIds: number[], allNodesById: Map<number, OsmNode>, osmWay: OsmWay }> {
// TODO FIXME: cap move length on points which are embedded into other ways (ev. disconnect them)
// TODO FIXME: if a new point has to be created, snap to already existing ways
// TODO FIXME: reuse points if they are the same in the target coordinates
const splitted = this.wayToReplaceId.split("/");
const type = splitted[0];
const idN = Number(splitted[1]);
if (idN < 0 || type !== "way") {
throw "Invalid ID to conflate: " + this.wayToReplaceId
}
const url = `${this.state.osmConnection._oauth_config.url}/api/0.6/${this.wayToReplaceId}/full`;
const rawData = await Utils.downloadJsonCached(url, 1000)
const parsed = OsmObject.ParseObjects(rawData.elements);
const allNodesById = new Map<number, OsmNode>()
const allNodes = parsed.filter(o => o.type === "node")
for (const node of allNodes) {
allNodesById.set(node.id, <OsmNode>node)
}
/**
* Allright! We know all the nodes of the original way and all the nodes of the target coordinates.
* For each of the target coordinates, we search the closest, already existing point and reuse this point
*/
const closestIds = []
const distances = []
for (const target of this.targetCoordinates) {
let closestDistance = undefined
let closestId = undefined;
for (const osmNode of allNodes) {
const cp = osmNode.centerpoint()
const d = GeoOperations.distanceBetween(target, [cp[1], cp[0]])
if (closestId === undefined || closestDistance > d) {
closestId = osmNode.id
closestDistance = d
}
}
closestIds.push(closestId)
distances.push(closestDistance)
}
// Next step: every closestId can only occur once in the list
for (let i = 0; i < closestIds.length; i++) {
const closestId = closestIds[i]
for (let j = i + 1; j < closestIds.length; j++) {
const otherClosestId = closestIds[j]
if (closestId !== otherClosestId) {
continue
}
// We have two occurences of 'closestId' - we only keep the closest instance!
const di = distances[i]
const dj = distances[j]
if (di < dj) {
closestIds[j] = undefined
} else {
closestIds[i] = undefined
}
}
}
const osmWay = <OsmWay>parsed[parsed.length - 1]
if (osmWay.type !== "way") {
throw "WEIRD: expected an OSM-way as last element here!"
}
return {closestIds, allNodesById, osmWay};
}
}

View file

@ -114,7 +114,16 @@ export class Changes {
}
public async applyAction(action: OsmChangeAction): Promise<void> {
const changes = await action.Perform(this)
this.applyChanges(await action.Perform(this))
}
public async applyActions(actions: OsmChangeAction[]) {
for (const action of actions) {
await this.applyAction(action)
}
}
public applyChanges(changes: ChangeDescription[]) {
console.log("Received changes:", changes)
this.pendingChanges.data.push(...changes);
this.pendingChanges.ping();
@ -126,6 +135,7 @@ export class Changes {
CreateNewNodeAction.registerIdRewrites(mappings)
}
/**
* UPload the selected changes to OSM.
* Returns 'true' if successfull and if they can be removed

View file

@ -14,6 +14,7 @@ import {QueryParameters} from "../Web/QueryParameters";
import * as personal from "../../assets/themes/personal/personal.json";
import FilterConfig from "../../Models/ThemeConfig/FilterConfig";
import ShowOverlayLayer from "../../UI/ShowDataLayer/ShowOverlayLayer";
import {Coord} from "@turf/turf";
/**
* Contains all the leaflet-map related state
@ -44,13 +45,7 @@ export default class MapState extends UserRelatedState {
/**
* The location as delivered by the GPS
*/
public currentGPSLocation: UIEventSource<{
latlng: { lat: number; lng: number };
accuracy: number;
}> = new UIEventSource<{
latlng: { lat: number; lng: number };
accuracy: number;
}>(undefined);
public currentGPSLocation: UIEventSource<Coordinates> = new UIEventSource<Coordinates>(undefined);
public readonly mainMapObject: BaseUIElement & MinimapObj;

28
UI/Base/AsyncLazy.ts Normal file
View file

@ -0,0 +1,28 @@
import BaseUIElement from "../BaseUIElement";
import {VariableUiElement} from "./VariableUIElement";
import {UIEventSource} from "../../Logic/UIEventSource";
import Loading from "./Loading";
export default class AsyncLazy extends BaseUIElement{
private readonly _f: () => Promise<BaseUIElement>;
constructor(f: () => Promise<BaseUIElement>) {
super();
this._f = f;
}
protected InnerConstructElement(): HTMLElement {
// The caching of the BaseUIElement will guarantee that _f will only be called once
return new VariableUiElement(
UIEventSource.FromPromise(this._f()).map(el => {
if(el === undefined){
return new Loading()
}
return el
})
).ConstructElement()
}
}

View file

@ -19,6 +19,7 @@ export interface MinimapOptions {
export interface MinimapObj {
readonly leafletMap: UIEventSource<any>,
installBounds(factor: number | BBox, showRange?: boolean) : void
TakeScreenshot(): Promise<any>;
}
export default class Minimap {

View file

@ -9,6 +9,7 @@ import {Map} from "leaflet";
import Minimap, {MinimapObj, MinimapOptions} from "./Minimap";
import {BBox} from "../../Logic/BBox";
import 'leaflet-polylineoffset'
import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter";
export default class MinimapImplementation extends BaseUIElement implements MinimapObj {
private static _nextId = 0;
@ -278,4 +279,10 @@ export default class MinimapImplementation extends BaseUIElement implements Mini
this.leafletMap.setData(map)
}
public async TakeScreenshot(){
const screenshotter = new SimpleMapScreenshoter();
screenshotter.addTo(this.leafletMap.data);
return await screenshotter.takeScreen('image')
}
}

View file

@ -14,6 +14,9 @@ import Toggle from "../Input/Toggle";
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
import {Utils} from "../../Utils";
import UserRelatedState from "../../Logic/State/UserRelatedState";
import Loc from "../../Models/Loc";
import BaseLayer from "../../Models/BaseLayer";
import FilteredLayer from "../../Models/FilteredLayer";
export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
@ -24,7 +27,10 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
layoutToUse: LayoutConfig,
osmConnection: OsmConnection,
featureSwitchShareScreen: UIEventSource<boolean>,
featureSwitchMoreQuests: UIEventSource<boolean>
featureSwitchMoreQuests: UIEventSource<boolean>,
locationControl: UIEventSource<Loc>,
backgroundLayer: UIEventSource<BaseLayer>,
filteredLayers: UIEventSource<FilteredLayer[]>
} & UserRelatedState) {
const layoutToUse = state.layoutToUse;
super(
@ -39,7 +45,8 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
layoutToUse: LayoutConfig,
osmConnection: OsmConnection,
featureSwitchShareScreen: UIEventSource<boolean>,
featureSwitchMoreQuests: UIEventSource<boolean>
featureSwitchMoreQuests: UIEventSource<boolean>,
locationControl: UIEventSource<Loc>, backgroundLayer: UIEventSource<BaseLayer>, filteredLayers: UIEventSource<FilteredLayer[]>
} & UserRelatedState,
isShown: UIEventSource<boolean>):
{ header: string | BaseUIElement; content: BaseUIElement }[] {
@ -77,7 +84,8 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
layoutToUse: LayoutConfig,
osmConnection: OsmConnection,
featureSwitchShareScreen: UIEventSource<boolean>,
featureSwitchMoreQuests: UIEventSource<boolean>
featureSwitchMoreQuests: UIEventSource<boolean>,
locationControl: UIEventSource<Loc>, backgroundLayer: UIEventSource<BaseLayer>, filteredLayers: UIEventSource<FilteredLayer[]>
} & UserRelatedState, currentTab: UIEventSource<number>, isShown: UIEventSource<boolean>) {
const tabs = FullWelcomePaneWithTabs.ConstructBaseTabs(state, isShown)

View file

@ -9,7 +9,6 @@ import Toggle from "../Input/Toggle";
import CreateNewNodeAction from "../../Logic/Osm/Actions/CreateNewNodeAction";
import {Tag} from "../../Logic/Tags/Tag";
import Loading from "../Base/Loading";
import OsmChangeAction from "../../Logic/Osm/Actions/OsmChangeAction";
import CreateNewWayAction from "../../Logic/Osm/Actions/CreateNewWayAction";
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
@ -26,6 +25,13 @@ import SpecialVisualizations, {SpecialVisualization} from "../SpecialVisualizati
import {FixedUiElement} from "../Base/FixedUiElement";
import Svg from "../../Svg";
import {Utils} from "../../Utils";
import Minimap from "../Base/Minimap";
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer";
import AllKnownLayers from "../../Customizations/AllKnownLayers";
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource";
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer";
import BaseLayer from "../../Models/BaseLayer";
import ReplaceGeometryAction from "../../Logic/Osm/Actions/ReplaceGeometryAction";
export interface ImportButtonState {
@ -38,6 +44,8 @@ export interface ImportButtonState {
feature: any,
minZoom: number,
state: {
backgroundLayer: UIEventSource<BaseLayer>;
filteredLayers: UIEventSource<FilteredLayer[]>;
featureSwitchUserbadge: UIEventSource<boolean>;
featurePipeline: FeaturePipeline;
allElements: ElementStorage;
@ -48,8 +56,14 @@ export interface ImportButtonState {
locationControl: UIEventSource<{ zoom: number }>
},
guiState: { filterViewIsOpened: UIEventSource<boolean> },
snapToLayers?: string[],
snapToLayersMaxDist?: number
snapSettings?: {
snapToLayers: string[],
snapToLayersMaxDist?: number
},
conflationSettings?: {
conflateWayId: string
}
}
export class ImportButtonSpecialViz implements SpecialVisualization {
@ -83,7 +97,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
#### Specifying which tags to copy or add
The first argument of the import button takes a \`;\`-seperated list of tags to add.
The argument \`tags\` of the import button takes a \`;\`-seperated list of tags to add.
${Utils.Special_visualizations_tagsToApplyHelpText}
@ -113,8 +127,9 @@ ${Utils.Special_visualizations_tagsToApplyHelpText}
doc: "How far the contributor must zoom in before being able to import the point",
defaultValue: "18"
}, {
name: "Snap onto layer(s)",
doc: "If a way of the given layer is closeby, will snap the new point onto this way (similar as preset might snap). To show multiple layers to snap onto, use a `;`-seperated list",
name: "Snap onto layer(s)/replace geometry with this other way",
doc: " - If the value corresponding with this key starts with 'way/' and the feature is a LineString or Polygon, the original OSM-way geometry will be changed to match the new geometry\n" +
" - If a way of the given layer(s) is closeby, will snap the new point onto this way (similar as preset might snap). To show multiple layers to snap onto, use a `;`-seperated list",
}, {
name: "snap max distance",
doc: "The maximum distance that this point will move to snap onto a layer (in meters)",
@ -130,7 +145,7 @@ ${Utils.Special_visualizations_tagsToApplyHelpText}
const id = tagSource.data.id;
const feature = state.allElements.ContainingFeatures.get(id)
let minZoom = args[4] == "" ? 18 : Number(args[4])
if(isNaN(minZoom)){
if (isNaN(minZoom)) {
console.warn("Invalid minzoom:", minZoom)
minZoom = 18
}
@ -145,13 +160,29 @@ ${Utils.Special_visualizations_tagsToApplyHelpText}
img = () => Svg.add_ui()
}
const snapToLayers = args[5]?.split(";").filter(s => s !== "")
const snapToLayersMaxDist = Number(args[6] ?? 6)
if (targetLayer === undefined) {
const e = "Target layer not defined: error in import button for theme: " + state.layoutToUse.id + ": layer " + args[0] + " not found"
console.error(e)
return new FixedUiElement(e).SetClass("alert")
let snapSettings = undefined
let conflationSettings = undefined
const possibleWayId = tagSource.data[args[5]]
if (possibleWayId?.startsWith("way/")) {
// This is a conflation
conflationSettings = {
conflateWayId: possibleWayId
}
} else {
const snapToLayers = args[5]?.split(";").filter(s => s !== "")
const snapToLayersMaxDist = Number(args[6] ?? 6)
if (targetLayer === undefined) {
const e = "Target layer not defined: error in import button for theme: " + state.layoutToUse.id + ": layer " + args[0] + " not found"
console.error(e)
return new FixedUiElement(e).SetClass("alert")
}
snapSettings = {
snapToLayers,
snapToLayersMaxDist
}
}
return new ImportButton(
@ -160,8 +191,8 @@ ${Utils.Special_visualizations_tagsToApplyHelpText}
feature, newTags, message, minZoom,
originalTags: tagSource,
targetLayer,
snapToLayers,
snapToLayersMaxDist
snapSettings,
conflationSettings
}
);
}
@ -201,7 +232,7 @@ export default class ImportButton extends Toggle {
const importClicked = new UIEventSource(false);
const importFlow = new Toggle(
new Lazy(() => ImportButton.createConfirmPanel(o, isImported, importClicked)),
ImportButton.createConfirmPanel(o, isImported, importClicked),
importButton,
importClicked
)
@ -228,7 +259,121 @@ export default class ImportButton extends Toggle {
)
}
public static createConfirmPanel(
public static createConfirmPanel(o: ImportButtonState,
isImported: UIEventSource<boolean>,
importClicked: UIEventSource<boolean>) {
const geometry = o.feature.geometry
if (geometry.type === "Point") {
return new Lazy(() => ImportButton.createConfirmPanelForPoint(o, isImported, importClicked))
}
if (geometry.type === "Polygon" || geometry.type == "LineString") {
return new Lazy(() => ImportButton.createConfirmForWay(o, isImported, importClicked))
}
console.error("Invalid type to import", geometry.type)
return new FixedUiElement("Invalid geometry type:" + geometry.type).SetClass("alert")
}
public static createConfirmForWay(o: ImportButtonState,
isImported: UIEventSource<boolean>,
importClicked: UIEventSource<boolean>): BaseUIElement {
const confirmationMap = Minimap.createMiniMap({
allowMoving: false,
background: o.state.backgroundLayer
})
confirmationMap.SetStyle("height: 20rem; overflow: hidden").SetClass("rounded-xl")
const relevantFeatures = Utils.NoNull([o.feature, o.state.allElements?.ContainingFeatures?.get(o.conflationSettings?.conflateWayId)])
// SHow all relevant data - including (eventually) the way of which the geometry will be replaced
new ShowDataMultiLayer({
leafletMap: confirmationMap.leafletMap,
enablePopups: false,
zoomToFeatures: true,
features: new StaticFeatureSource(relevantFeatures, false),
allElements: o.state.allElements,
layers: o.state.filteredLayers
})
const theme = o.state.layoutToUse.id
const changes = o.state.changes
let confirm: () => Promise<string>
if (o.conflationSettings !== undefined) {
let replaceGeometryAction = new ReplaceGeometryAction(
o.state,
o.feature,
o.conflationSettings.conflateWayId,
{
theme: o.state.layoutToUse.id,
newTags: o.newTags.data
}
)
replaceGeometryAction.GetPreview().then(changePreview => {
new ShowDataLayer({
leafletMap: confirmationMap.leafletMap,
enablePopups: false,
zoomToFeatures: false,
features: changePreview,
allElements: o.state.allElements,
layerToShow: AllKnownLayers.sharedLayers.get("conflation")
})
})
confirm = async () => {
changes.applyAction (replaceGeometryAction)
return o.feature.properties.id
}
} else {
confirm = async () => {
const geom = o.feature.geometry
let coordinates: [number, number][]
if (geom.type === "LineString") {
coordinates = geom.coordinates
} else if (geom.type === "Polygon") {
coordinates = geom.coordinates[0]
}
const action = new CreateNewWayAction(o.newTags.data, coordinates.map(lngLat => ({
lat: lngLat[1],
lon: lngLat[0]
})), {theme})
return action.newElementId
}
}
const confirmButton = new SubtleButton(o.image(), o.message)
confirmButton.onClick(async () => {
{
if (isImported.data) {
return
}
o.originalTags.data["_imported"] = "yes"
o.originalTags.ping() // will set isImported as per its definition
const idToSelect = await confirm()
o.state.selectedElement.setData(o.state.allElements.ContainingFeatures.get(idToSelect))
}
})
const cancel = new SubtleButton(Svg.close_ui(), Translations.t.general.cancel).onClick(() => {
importClicked.setData(false)
})
return new Combine([confirmationMap, confirmButton, cancel]).SetClass("flex flex-col")
}
public static createConfirmPanelForPoint(
o: ImportButtonState,
isImported: UIEventSource<boolean>,
importClicked: UIEventSource<boolean>): BaseUIElement {
@ -239,39 +384,43 @@ export default class ImportButton extends Toggle {
}
o.originalTags.data["_imported"] = "yes"
o.originalTags.ping() // will set isImported as per its definition
const newElementAction = ImportButton.createAddActionForFeature(o.newTags.data, o.feature, o.state.layoutToUse.id)
const geometry = o.feature.geometry
const lat = geometry.coordinates[1]
const lon = geometry.coordinates[0];
const newElementAction = new CreateNewNodeAction(o.newTags.data, lat, lon, {
theme: o.state.layoutToUse.id,
changeType: "import"
})
await o.state.changes.applyAction(newElementAction)
o.state.selectedElement.setData(o.state.allElements.ContainingFeatures.get(
newElementAction.newElementId
))
console.log("Did set selected element to", o.state.allElements.ContainingFeatures.get(
newElementAction.newElementId
))
}
function cancel() {
importClicked.setData(false)
}
if (o.feature.geometry.type === "Point") {
const presetInfo = <PresetInfo>{
tags: o.newTags.data,
icon: o.image,
description: o.description,
layerToAddTo: o.targetLayer,
name: o.message,
title: o.message,
preciseInput: { snapToLayers: o.snapToLayers,
maxSnapDistance: o.snapToLayersMaxDist}
const presetInfo = <PresetInfo>{
tags: o.newTags.data,
icon: o.image,
description: o.description,
layerToAddTo: o.targetLayer,
name: o.message,
title: o.message,
preciseInput: {
snapToLayers: o.snapSettings?.snapToLayers,
maxSnapDistance: o.snapSettings?.snapToLayersMaxDist
}
const [lon, lat] = o.feature.geometry.coordinates
console.log("Creating an import dialog at location", lon, lat)
return new ConfirmLocationOfPoint(o.state, o.guiState.filterViewIsOpened, presetInfo, Translations.W(o.message), {
lon,
lat
}, confirm, cancel)
}
const [lon, lat] = o.feature.geometry.coordinates
return new ConfirmLocationOfPoint(o.state, o.guiState.filterViewIsOpened, presetInfo, Translations.W(o.message), {
lon,
lat
}, confirm, cancel)
}
@ -279,41 +428,4 @@ export default class ImportButton extends Toggle {
const type = feature.geometry.type
return type === "Point" || type === "LineString" || (type === "Polygon" && feature.geometry.coordinates.length === 1)
}
private static createAddActionForFeature(newTags: Tag[], feature: any, theme: string):
OsmChangeAction & { newElementId: string } {
const geometry = feature.geometry
const type = geometry.type
if (type === "Point") {
const lat = geometry.coordinates[1]
const lon = geometry.coordinates[0];
return new CreateNewNodeAction(newTags, lat, lon, {
theme,
changeType: "import"
})
}
if (type === "LineString") {
return new CreateNewWayAction(
newTags,
geometry.coordinates.map(coor => ({lon: coor[0], lat: coor[1]})),
{
theme
}
)
}
if (type === "Polygon") {
return new CreateNewWayAction(
newTags,
geometry.coordinates[0].map(coor => ({lon: coor[0], lat: coor[1]})),
{
theme
}
)
}
return undefined;
}
}

View file

@ -14,6 +14,8 @@ import Loc from "../../Models/Loc";
import {BBox} from "../../Logic/BBox";
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
import FilteredLayer from "../../Models/FilteredLayer";
import BaseLayer from "../../Models/BaseLayer";
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
export default class LeftControls extends Combine {
@ -26,7 +28,9 @@ export default class LeftControls extends Combine {
featureSwitchEnableExport: UIEventSource<boolean>,
featureSwitchExportAsPdf: UIEventSource<boolean>,
filteredLayers: UIEventSource<FilteredLayer[]>,
featureSwitchFilter: UIEventSource<boolean>
featureSwitchFilter: UIEventSource<boolean>,
backgroundLayer: UIEventSource<BaseLayer>,
osmConnection: OsmConnection
},
guiState: {
downloadControlIsOpened: UIEventSource<boolean>,

View file

@ -4,17 +4,30 @@ import MapControlButton from "../MapControlButton";
import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler";
import Svg from "../../Svg";
import MapState from "../../Logic/State/MapState";
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer";
import AllKnownLayers from "../../Customizations/AllKnownLayers";
export default class RightControls extends Combine {
constructor(state:MapState) {
const geolocatioHandler = new GeoLocationHandler(
state.currentGPSLocation,
state.leafletMap,
state.layoutToUse
)
new ShowDataLayer({
layerToShow: AllKnownLayers.sharedLayers.get("gps_location"),
leafletMap: state.leafletMap,
enablePopups: true,
features: geolocatioHandler.currentLocation
})
const geolocationButton = new Toggle(
new MapControlButton(
new GeoLocationHandler(
state.currentGPSLocation,
state.leafletMap,
state.layoutToUse
), {
geolocatioHandler
, {
dontStyle: true
}
),

View file

@ -8,11 +8,14 @@ import Toggle from "../Input/Toggle";
import Translations from "../i18n/Translations";
import BaseUIElement from "../BaseUIElement";
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
import MapState from "../../Logic/State/MapState";
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
import Loc from "../../Models/Loc";
import BaseLayer from "../../Models/BaseLayer";
import FilteredLayer from "../../Models/FilteredLayer";
export default class ShareScreen extends Combine {
constructor(state: MapState) {
constructor(state: {layoutToUse: LayoutConfig, locationControl: UIEventSource<Loc>, backgroundLayer: UIEventSource<BaseLayer>, filteredLayers: UIEventSource<FilteredLayer[]>}) {
const layout = state?.layoutToUse;
const tr = Translations.t.general.sharescreen;

View file

@ -6,9 +6,6 @@ import FullWelcomePaneWithTabs from "./BigComponents/FullWelcomePaneWithTabs";
import MapControlButton from "./MapControlButton";
import Svg from "../Svg";
import Toggle from "./Input/Toggle";
import Hash from "../Logic/Web/Hash";
import {QueryParameters} from "../Logic/Web/QueryParameters";
import Constants from "../Models/Constants";
import UserBadge from "./BigComponents/UserBadge";
import SearchAndGo from "./BigComponents/SearchAndGo";
import Link from "./Base/Link";
@ -24,77 +21,7 @@ import Translations from "./i18n/Translations";
import SimpleAddUI from "./BigComponents/SimpleAddUI";
import StrayClickHandler from "../Logic/Actors/StrayClickHandler";
import Lazy from "./Base/Lazy";
export class DefaultGuiState {
public readonly welcomeMessageIsOpened : UIEventSource<boolean>;
public readonly downloadControlIsOpened: UIEventSource<boolean>;
public readonly filterViewIsOpened: UIEventSource<boolean>;
public readonly copyrightViewIsOpened: UIEventSource<boolean>;
public readonly welcomeMessageOpenedTab: UIEventSource<number>
public readonly allFullScreenStates: UIEventSource<boolean>[] = []
static state: DefaultGuiState;
constructor() {
this.welcomeMessageOpenedTab = UIEventSource.asFloat(QueryParameters.GetQueryParameter(
"tab",
"0",
`The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >${Constants.userJourney.mapCompleteHelpUnlock} changesets)`
));
this.welcomeMessageIsOpened = QueryParameters.GetBooleanQueryParameter(
"welcome-control-toggle",
"false",
"Whether or not the welcome panel is shown"
)
this.downloadControlIsOpened = QueryParameters.GetBooleanQueryParameter(
"download-control-toggle",
"false",
"Whether or not the download panel is shown"
)
this.filterViewIsOpened = QueryParameters.GetBooleanQueryParameter(
"filter-toggle",
"false",
"Whether or not the filter view is shown"
)
this.copyrightViewIsOpened = QueryParameters.GetBooleanQueryParameter(
"copyright-toggle",
"false",
"Whether or not the copyright view is shown"
)
if(Hash.hash.data === "download"){
this.downloadControlIsOpened.setData(true)
}
if(Hash.hash.data === "filters"){
this.filterViewIsOpened.setData(true)
}
if(Hash.hash.data === "copyright"){
this.copyrightViewIsOpened.setData(true)
}
if(Hash.hash.data === "" || Hash.hash.data === undefined || Hash.hash.data === "welcome"){
this.welcomeMessageIsOpened.setData(true)
}
this.allFullScreenStates.push(this.downloadControlIsOpened, this.filterViewIsOpened, this.copyrightViewIsOpened, this.welcomeMessageIsOpened)
for (let i = 0; i < this.allFullScreenStates.length; i++){
const fullScreenState = this.allFullScreenStates[i];
for (let j = 0; j < this.allFullScreenStates.length; j++){
if(i == j){
continue
}
const otherState = this.allFullScreenStates[j];
fullScreenState.addCallbackAndRunD(isOpened => {
if(isOpened){
otherState.setData(false)
}
})
}
}
}
}
import {DefaultGuiState} from "./DefaultGuiState";
/**

74
UI/DefaultGuiState.ts Normal file
View file

@ -0,0 +1,74 @@
import {UIEventSource} from "../Logic/UIEventSource";
import {QueryParameters} from "../Logic/Web/QueryParameters";
import Constants from "../Models/Constants";
import Hash from "../Logic/Web/Hash";
export class DefaultGuiState {
public readonly welcomeMessageIsOpened: UIEventSource<boolean>;
public readonly downloadControlIsOpened: UIEventSource<boolean>;
public readonly filterViewIsOpened: UIEventSource<boolean>;
public readonly copyrightViewIsOpened: UIEventSource<boolean>;
public readonly welcomeMessageOpenedTab: UIEventSource<number>
public readonly allFullScreenStates: UIEventSource<boolean>[] = []
static state: DefaultGuiState;
constructor() {
this.welcomeMessageOpenedTab = UIEventSource.asFloat(QueryParameters.GetQueryParameter(
"tab",
"0",
`The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >${Constants.userJourney.mapCompleteHelpUnlock} changesets)`
));
this.welcomeMessageIsOpened = QueryParameters.GetBooleanQueryParameter(
"welcome-control-toggle",
"false",
"Whether or not the welcome panel is shown"
)
this.downloadControlIsOpened = QueryParameters.GetBooleanQueryParameter(
"download-control-toggle",
"false",
"Whether or not the download panel is shown"
)
this.filterViewIsOpened = QueryParameters.GetBooleanQueryParameter(
"filter-toggle",
"false",
"Whether or not the filter view is shown"
)
this.copyrightViewIsOpened = QueryParameters.GetBooleanQueryParameter(
"copyright-toggle",
"false",
"Whether or not the copyright view is shown"
)
if (Hash.hash.data === "download") {
this.downloadControlIsOpened.setData(true)
}
if (Hash.hash.data === "filters") {
this.filterViewIsOpened.setData(true)
}
if (Hash.hash.data === "copyright") {
this.copyrightViewIsOpened.setData(true)
}
if (Hash.hash.data === "" || Hash.hash.data === undefined || Hash.hash.data === "welcome") {
this.welcomeMessageIsOpened.setData(true)
}
this.allFullScreenStates.push(this.downloadControlIsOpened, this.filterViewIsOpened, this.copyrightViewIsOpened, this.welcomeMessageIsOpened)
for (let i = 0; i < this.allFullScreenStates.length; i++) {
const fullScreenState = this.allFullScreenStates[i];
for (let j = 0; j < this.allFullScreenStates.length; j++) {
if (i == j) {
continue
}
const otherState = this.allFullScreenStates[j];
fullScreenState.addCallbackAndRunD(isOpened => {
if (isOpened) {
otherState.setData(false)
}
})
}
}
}
}

View file

@ -1,9 +1,6 @@
import jsPDF from "jspdf";
import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter";
import {UIEventSource} from "../Logic/UIEventSource";
import Minimap from "./Base/Minimap";
import Minimap, {MinimapObj} from "./Base/Minimap";
import Loc from "../Models/Loc";
import BaseLayer from "../Models/BaseLayer";
import {FixedUiElement} from "./Base/FixedUiElement";
@ -14,7 +11,6 @@ import LayoutConfig from "../Models/ThemeConfig/LayoutConfig";
import FeaturePipeline from "../Logic/FeatureSource/FeaturePipeline";
import ShowDataLayer from "./ShowDataLayer/ShowDataLayer";
import {BBox} from "../Logic/BBox";
import ShowOverlayLayer from "./ShowDataLayer/ShowOverlayLayer";
/**
* Creates screenshoter to take png screenshot
* Creates jspdf and downloads it
@ -63,14 +59,12 @@ export default class ExportPDF {
location: new UIEventSource<Loc>(loc), // We remove the link between the old and the new UI-event source as moving the map while the export is running fucks up the screenshot
background: options.background,
allowMoving: false,
onFullyLoaded: leaflet => window.setTimeout(() => {
onFullyLoaded: _ => window.setTimeout(() => {
if (self._screenhotTaken) {
return;
}
try {
self.CreatePdf(leaflet)
self.CreatePdf(minimap)
.then(() => self.cleanup())
.catch(() => self.cleanup())
} catch (e) {
@ -112,20 +106,17 @@ export default class ExportPDF {
this._screenhotTaken = true;
}
private async CreatePdf(leaflet: L.Map) {
private async CreatePdf(minimap: MinimapObj) {
console.log("PDF creation started")
const t = Translations.t.general.pdf;
const layout = this._layout
const screenshotter = new SimpleMapScreenshoter();
//minimap op index.html -> hidden daar alles op doen en dan weg
//minimap - leaflet map ophalen - boundaries ophalen - State.state.featurePipeline
screenshotter.addTo(leaflet);
let doc = new jsPDF('landscape');
const image = (await screenshotter.takeScreen('image'))
const image = await minimap.TakeScreenshot()
// @ts-ignore
doc.addImage(image, 'PNG', 0, 0, this.mapW, this.mapH);

View file

@ -167,6 +167,9 @@ export default class LocationInput extends InputElement<Loc> implements MinimapO
installBounds(factor: number | BBox, showRange?: boolean): void {
this.map.installBounds(factor, showRange)
}
TakeScreenshot(): Promise<any> {
return this.map.TakeScreenshot()
}
protected InnerConstructElement(): HTMLElement {
try {

View file

@ -58,7 +58,6 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
for (const groupName of allGroupNames) {
const questions = layerConfig.tagRenderings.filter(tr => tr.group === groupName)
const questionBox = new QuestionBox(tags, questions, layerConfig.units);
console.log("Groupname:", groupName)
questionBoxes.set(groupName, questionBox)
}
}

View file

@ -155,7 +155,6 @@ export default class ShowDataLayer {
continue
}
try {
if ((feat.geometry.type === "LineString" || feat.geometry.type === "MultiLineString")) {
const self = this;
const coords = L.GeoJSON.coordsToLatLngs(feat.geometry.coordinates)
@ -190,9 +189,10 @@ export default class ShowDataLayer {
if (options.zoomToFeatures ?? false) {
try {
mp.fitBounds(this.geoLayer.getBounds(), {animate: false})
const bounds = this.geoLayer.getBounds()
mp.fitBounds(bounds, {animate: false})
} catch (e) {
console.error(e)
console.debug("Invalid bounds",e)
}
}

View file

@ -20,7 +20,7 @@ import Histogram from "./BigComponents/Histogram";
import Loc from "../Models/Loc";
import {Utils} from "../Utils";
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
import ImportButton, {ImportButtonSpecialViz} from "./BigComponents/ImportButton";
import {ImportButtonSpecialViz} from "./BigComponents/ImportButton";
import {Tag} from "../Logic/Tags/Tag";
import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource";
import ShowDataMultiLayer from "./ShowDataLayer/ShowDataMultiLayer";
@ -38,9 +38,9 @@ import {SubtleButton} from "./Base/SubtleButton";
import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction";
import {And} from "../Logic/Tags/And";
import Toggle from "./Input/Toggle";
import {DefaultGuiState} from "./DefaultGUI";
import Img from "./Base/Img";
import FilteredLayer from "../Models/FilteredLayer";
import {DefaultGuiState} from "./DefaultGuiState";
export interface SpecialVisualization {
funcName: string,

View file

@ -8,7 +8,7 @@ import {Utils} from "../Utils";
import {VariableUiElement} from "./Base/VariableUIElement";
import Combine from "./Base/Combine";
import BaseUIElement from "./BaseUIElement";
import {DefaultGuiState} from "./DefaultGUI";
import {DefaultGuiState} from "./DefaultGuiState";
export class SubstitutedTranslation extends VariableUiElement {

View file

@ -0,0 +1,32 @@
{
"id": "conflation",
"description": "This is a special meta_layer which render geometry-changes for inspection",
"minzoom": 1,
"source": {
"osmTags": {
"or": ["move=yes","newpoint=yes"]
}
},
"name": "Conflation",
"title": "Conflation",
"mapRendering": [
{
"location": "point",
"icon": "addSmall:#000",
"iconSize": "10,10,center"
},
{
"location": "end",
"icon": "circle:#0f0",
"iconSize": "10,10,center"
},{
"location": "start",
"icon": "square:#f00",
"iconSize": "10,10,center"
},
{
"width": "3",
"color": "#00f"
}
]
}

View file

@ -0,0 +1,15 @@
{
"id": "gps_location",
"description": "Meta layer showing the current location of the user",
"minzoom": 0,
"source": {
"osmTags": "user:location=yes"
},
"mapRendering": [
{
"icon": "crosshair:#00f",
"iconSize": "40,40,center",
"location": "point"
}
]
}

View file

@ -1,35 +1,19 @@
{
"id": "home_location",
"description": "Meta layer showing the home location of the user",
"minzoom": 0,
"source": {
"osmTags": "user:home=yes"
},
"icon": {
"id": "home_location",
"description": "Meta layer showing the home location of the user",
"minzoom": 0,
"source": {
"osmTags": "user:home=yes"
},
"mapRendering": [
{
"icon": {
"render": "circle:white;./assets/svg/home.svg"
},
"iconSize": {
},
"iconSize": {
"render": "20,20,center"
},
"color": {
"render": "#00f"
},
"mapRendering": [
{
"icon": {
"render": "circle:white;./assets/svg/home.svg"
},
"iconSize": {
"render": "20,20,center"
},
"location": [
"point"
]
},
{
"color": {
"render": "#00f"
}
}
]
},
"location": "point"
}
]
}

View file

@ -235,11 +235,6 @@
}
],
"mapRendering": [
{
"location": [
"point"
]
},
{
"color": {
"render": "#ff7392",

View file

@ -38,7 +38,9 @@
"override": {
"calculatedTags": [
"_is_part_of_building=feat.get('parent_ways')?.some(p => p.building !== undefined && p.building !== '') ?? false",
"_is_part_of_landuse=feat.get('parent_ways')?.some(p => (p.landuse !== undefined && p.landuse !== '') || (p.natural !== undefined && p.natural !== '')) ?? false"
"_is_part_of_building_passage=feat.get('parent_ways')?.some(p => p.tunnel === 'building_passage') ?? false",
"_is_part_of_highway=!feat.get('is_part_of_building_passage') && (feat.get('parent_ways')?.some(p => p.highway !== undefined && p.highway !== '') ?? false)",
"_is_part_of_landuse=feat.get('parent_ways')?.some(p => (p.landuse !== undefined && p.landuse !== '') || (p.natural !== undefined && p.natural !== '')) ?? false"
]
}
},
@ -674,7 +676,7 @@
"mappings": [
{
"if": "_overlaps_with!=null",
"then": "Cannot be imported directly, there is a nearly identical building geometry in OpenStreetMap"
"then": "{import_button(OSM-buildings,building=$building; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref, Replace the geometry in OpenStreetMap,,,_osm_obj:id)}"
}
]
},

View file

@ -9,10 +9,11 @@ import {Utils} from "./Utils";
import AllThemesGui from "./UI/AllThemesGui";
import DetermineLayout from "./Logic/DetermineLayout";
import LayoutConfig from "./Models/ThemeConfig/LayoutConfig";
import DefaultGUI, {DefaultGuiState} from "./UI/DefaultGUI";
import DefaultGUI from "./UI/DefaultGUI";
import State from "./State";
import AvailableBaseLayersImplementation from "./Logic/Actors/AvailableBaseLayersImplementation";
import ShowOverlayLayerImplementation from "./UI/ShowDataLayer/ShowOverlayLayerImplementation";
import {DefaultGuiState} from "./UI/DefaultGuiState";
// Workaround for a stupid crash: inject some functions which would give stupid circular dependencies or crash the other nodejs scripts running from console
MinimapImplementation.initialize()

165
test.ts
View file

@ -1,139 +1,26 @@
import {Utils} from "./Utils";
import FullNodeDatabaseSource from "./Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource";
const data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<osm version=\"0.6\" generator=\"CGImap 0.8.5 (2610109 spike-08.openstreetmap.org)\" copyright=\"OpenStreetMap and contributors\" attribution=\"http://www.openstreetmap.org/copyright\" license=\"http://opendatacommons.org/licenses/odbl/1-0/\">\n" +
" <bounds minlat=\"51.2154864\" minlon=\"3.2176208\" maxlat=\"51.2163466\" maxlon=\"3.2189941\"/>\n" +
" <node id=\"315739208\" visible=\"true\" version=\"6\" changeset=\"11063832\" timestamp=\"2012-03-22T15:12:47Z\" user=\"zors1843\" uid=\"233248\" lat=\"51.2149470\" lon=\"3.2183010\"/>\n" +
" <node id=\"315739215\" visible=\"true\" version=\"8\" changeset=\"7350988\" timestamp=\"2011-02-21T09:50:46Z\" user=\"zors1843\" uid=\"233248\" lat=\"51.2160281\" lon=\"3.2174966\"/>\n" +
" <node id=\"315739216\" visible=\"true\" version=\"7\" changeset=\"11037951\" timestamp=\"2012-03-20T06:38:28Z\" user=\"zors1843\" uid=\"233248\" lat=\"51.2152977\" lon=\"3.2195995\"/>\n" +
" <node id=\"315739242\" visible=\"true\" version=\"2\" changeset=\"11037951\" timestamp=\"2012-03-20T06:38:29Z\" user=\"zors1843\" uid=\"233248\" lat=\"51.2164491\" lon=\"3.2187218\"/>\n" +
" <node id=\"315739243\" visible=\"true\" version=\"9\" changeset=\"11037951\" timestamp=\"2012-03-20T06:38:29Z\" user=\"zors1843\" uid=\"233248\" lat=\"51.2166162\" lon=\"3.2182807\"/>\n" +
" <node id=\"1682824800\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2154405\" lon=\"3.2193489\"/>\n" +
" <node id=\"1682824805\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2154354\" lon=\"3.2193255\"/>\n" +
" <node id=\"1682824813\" visible=\"true\" version=\"3\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2155117\" lon=\"3.2192071\"/>\n" +
" <node id=\"1682824815\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2154693\" lon=\"3.2193015\"/>\n" +
" <node id=\"1682824817\" visible=\"true\" version=\"3\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2153821\" lon=\"3.2193628\"/>\n" +
" <node id=\"1682832761\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2159858\" lon=\"3.2190646\"/>\n" +
" <node id=\"1682832762\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2157564\" lon=\"3.2191917\"/>\n" +
" <node id=\"1682832764\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2157971\" lon=\"3.2187018\"/>\n" +
" <node id=\"1682832775\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2153841\" lon=\"3.2186735\"/>\n" +
" <node id=\"1682832777\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2156194\" lon=\"3.2188368\"/>\n" +
" <node id=\"1682832780\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2153013\" lon=\"3.2188337\"/>\n" +
" <node id=\"1682832782\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2157716\" lon=\"3.2190346\"/>\n" +
" <node id=\"1682832784\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2153225\" lon=\"3.2189135\"/>\n" +
" <node id=\"1682832786\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2154508\" lon=\"3.2187894\"/>\n" +
" <node id=\"1682832789\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2159305\" lon=\"3.2188530\"/>\n" +
" <node id=\"1682832791\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2157323\" lon=\"3.2180624\"/>\n" +
" <node id=\"1682832793\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:21Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2159622\" lon=\"3.2188025\"/>\n" +
" <node id=\"1682832795\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2159076\" lon=\"3.2189725\"/>\n" +
" <node id=\"1682832804\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2157453\" lon=\"3.2186489\"/>\n" +
" <node id=\"1682832808\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2157935\" lon=\"3.2186116\"/>\n" +
" <node id=\"1682832813\" visible=\"true\" version=\"3\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2152199\" lon=\"3.2186903\"/>\n" +
" <node id=\"1682832815\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2160796\" lon=\"3.2189893\"/>\n" +
" <node id=\"1682832817\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2154484\" lon=\"3.2188760\"/>\n" +
" <node id=\"1682832818\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2152395\" lon=\"3.2187686\"/>\n" +
" <node id=\"1682832819\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2153621\" lon=\"3.2185962\"/>\n" +
" <node id=\"1682832820\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2155109\" lon=\"3.2184903\"/>\n" +
" <node id=\"1682832821\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2155922\" lon=\"3.2187693\"/>\n" +
" <node id=\"1682832825\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2154945\" lon=\"3.2191530\"/>\n" +
" <node id=\"1682832826\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2157122\" lon=\"3.2192275\"/>\n" +
" <node id=\"1682832827\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2154505\" lon=\"3.2187683\"/>\n" +
" <node id=\"1682832828\" visible=\"true\" version=\"3\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2160312\" lon=\"3.2188379\"/>\n" +
" <node id=\"1682832829\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2160071\" lon=\"3.2188563\"/>\n" +
" <node id=\"1682832830\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2158432\" lon=\"3.2189346\"/>\n" +
" <node id=\"1682832831\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2157027\" lon=\"3.2184476\"/>\n" +
" <node id=\"1682832832\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2159424\" lon=\"3.2189018\"/>\n" +
" <node id=\"1682832833\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:22Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2156684\" lon=\"3.2191183\"/>\n" +
" <node id=\"1682832834\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2158295\" lon=\"3.2185829\"/>\n" +
" <node id=\"1682832835\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2156820\" lon=\"3.2180867\"/>\n" +
" <node id=\"1682832836\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2155219\" lon=\"3.2191313\"/>\n" +
" <node id=\"1682832837\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2158056\" lon=\"3.2190810\"/>\n" +
" <node id=\"1682832838\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2157585\" lon=\"3.2190453\"/>\n" +
" <node id=\"1682832839\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2153634\" lon=\"3.2186009\"/>\n" +
" <node id=\"1682832840\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2157257\" lon=\"3.2185567\"/>\n" +
" <node id=\"1682832841\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2159207\" lon=\"3.2190210\"/>\n" +
" <node id=\"1682832842\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2160295\" lon=\"3.2190298\"/>\n" +
" <node id=\"1682832843\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2156835\" lon=\"3.2183577\"/>\n" +
" <node id=\"1682832846\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2158946\" lon=\"3.2189083\"/>\n" +
" <node id=\"1682832848\" visible=\"true\" version=\"1\" changeset=\"11037951\" timestamp=\"2012-03-20T06:38:23Z\" user=\"zors1843\" uid=\"233248\" lat=\"51.2162257\" lon=\"3.2189152\"/>\n" +
" <node id=\"1682832850\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2156455\" lon=\"3.2189033\"/>\n" +
" <node id=\"1682832851\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2157814\" lon=\"3.2191010\"/>\n" +
" <node id=\"1682832852\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2158297\" lon=\"3.2191463\"/>\n" +
" <node id=\"1682832853\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2153884\" lon=\"3.2186848\"/>\n" +
" <node id=\"1682832854\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2155190\" lon=\"3.2191206\"/>\n" +
" <node id=\"1682832856\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2156298\" lon=\"3.2190100\"/>\n" +
" <node id=\"1682832858\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2156874\" lon=\"3.2190086\"/>\n" +
" <node id=\"1682832859\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2159818\" lon=\"3.2188688\"/>\n" +
" <node id=\"1682832860\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2159413\" lon=\"3.2190997\"/>\n" +
" <node id=\"1682832861\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2159831\" lon=\"3.2187874\"/>\n" +
" <node id=\"1682832862\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:23Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2157432\" lon=\"3.2189602\"/>\n" +
" <node id=\"1682837112\" visible=\"true\" version=\"3\" changeset=\"113131738\" timestamp=\"2021-10-29T16:20:51Z\" user=\"Pieter Vander Vennet\" uid=\"3818858\" lat=\"51.2160077\" lon=\"3.2187651\"/>\n" +
" <node id=\"1682837113\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2161667\" lon=\"3.2187271\"/>\n" +
" <node id=\"1682837114\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2162728\" lon=\"3.2185965\"/>\n" +
" <node id=\"1682837115\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2161349\" lon=\"3.2185984\"/>\n" +
" <node id=\"1682837120\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2163153\" lon=\"3.2187451\"/>\n" +
" <node id=\"1682837122\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2164091\" lon=\"3.2186697\"/>\n" +
" <node id=\"1682837124\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2161535\" lon=\"3.2183166\"/>\n" +
" <node id=\"1682837126\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2162789\" lon=\"3.2187745\"/>\n" +
" <node id=\"1682837128\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2161929\" lon=\"3.2185504\"/>\n" +
" <node id=\"1682837130\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2162296\" lon=\"3.2186760\"/>\n" +
" <node id=\"1682837132\" visible=\"true\" version=\"3\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2165327\" lon=\"3.2183323\"/>\n" +
" <node id=\"1682837133\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2163805\" lon=\"3.2186936\"/>\n" +
" <node id=\"1682837134\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2162456\" lon=\"3.2186644\"/>\n" +
" <node id=\"1682837135\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2162404\" lon=\"3.2188053\"/>\n" +
" <node id=\"1682837136\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2162428\" lon=\"3.2184412\"/>\n" +
" <node id=\"1682837138\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2162938\" lon=\"3.2185770\"/>\n" +
" <node id=\"1682837142\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2161321\" lon=\"3.2183620\"/>\n" +
" <node id=\"1682837143\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2161991\" lon=\"3.2188378\"/>\n" +
" <node id=\"1682837145\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2160656\" lon=\"3.2189444\"/>\n" +
" <node id=\"1682837146\" visible=\"true\" version=\"3\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2163975\" lon=\"3.2181513\"/>\n" +
" <node id=\"1682837147\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2161668\" lon=\"3.2185723\"/>\n" +
" <node id=\"1682837148\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2161842\" lon=\"3.2187118\"/>\n" +
" <node id=\"1682837149\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2160893\" lon=\"3.2186362\"/>\n" +
" <node id=\"1682837150\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2161619\" lon=\"3.2188670\"/>\n" +
" <node id=\"1682874140\" visible=\"true\" version=\"2\" changeset=\"50433000\" timestamp=\"2017-07-20T13:11:32Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2163477\" lon=\"3.2191243\"/>\n" +
" <node id=\"1682874141\" visible=\"true\" version=\"2\" changeset=\"50433000\" timestamp=\"2017-07-20T13:11:32Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2160579\" lon=\"3.2190935\"/>\n" +
" <node id=\"1682874142\" visible=\"true\" version=\"2\" changeset=\"50433000\" timestamp=\"2017-07-20T13:11:32Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2161979\" lon=\"3.2193991\"/>\n" +
" <node id=\"1682874143\" visible=\"true\" version=\"2\" changeset=\"50433000\" timestamp=\"2017-07-20T13:11:32Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2162760\" lon=\"3.2189217\"/>\n" +
" <node id=\"1682874144\" visible=\"true\" version=\"2\" changeset=\"50433000\" timestamp=\"2017-07-20T13:11:32Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2163468\" lon=\"3.2188607\"/>\n" +
" <node id=\"1682874150\" visible=\"true\" version=\"2\" changeset=\"50433000\" timestamp=\"2017-07-20T13:11:32Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2163835\" lon=\"3.2190937\"/>\n" +
" <node id=\"1682874151\" visible=\"true\" version=\"2\" changeset=\"50433000\" timestamp=\"2017-07-20T13:11:32Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2164206\" lon=\"3.2190633\"/>\n" +
" <node id=\"1682874161\" visible=\"true\" version=\"2\" changeset=\"50433000\" timestamp=\"2017-07-20T13:11:33Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2161339\" lon=\"3.2194350\"/>\n" +
" <node id=\"1682874164\" visible=\"true\" version=\"2\" changeset=\"50433000\" timestamp=\"2017-07-20T13:11:33Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2163293\" lon=\"3.2191391\"/>\n" +
" <node id=\"1682874165\" visible=\"true\" version=\"2\" changeset=\"50433000\" timestamp=\"2017-07-20T13:11:33Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2160741\" lon=\"3.2191659\"/>\n" +
" <node id=\"1682874166\" visible=\"true\" version=\"2\" changeset=\"50433000\" timestamp=\"2017-07-20T13:11:33Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2163108\" lon=\"3.2188917\"/>\n" +
" <node id=\"1682875368\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2159045\" lon=\"3.2178572\"/>\n" +
" <node id=\"1682875370\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2158354\" lon=\"3.2179572\"/>\n" +
" <node id=\"1682875372\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2158939\" lon=\"3.2176815\"/>\n" +
" <node id=\"1682875373\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:24Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2158483\" lon=\"3.2179326\"/>\n" +
" <node id=\"1682875374\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2157153\" lon=\"3.2177946\"/>\n" +
" <node id=\"1682875381\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2158748\" lon=\"3.2178159\"/>\n" +
" <node id=\"1682875385\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2158683\" lon=\"3.2179618\"/>\n" +
" <node id=\"1682875387\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2158782\" lon=\"3.2179443\"/>\n" +
" <node id=\"1682875389\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2158141\" lon=\"3.2177312\"/>\n" +
" <node id=\"1682875395\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2157649\" lon=\"3.2177632\"/>\n" +
" <node id=\"1682876085\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2155829\" lon=\"3.2178849\"/>\n" +
" <node id=\"1682876086\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2156324\" lon=\"3.2178513\"/>\n" +
" <node id=\"1682876088\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2156714\" lon=\"3.2180361\"/>\n" +
" <node id=\"1682876091\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2156926\" lon=\"3.2178103\"/>\n" +
" <node id=\"1682876092\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2156297\" lon=\"3.2178592\"/>\n" +
" <node id=\"1682876099\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2157351\" lon=\"3.2180027\"/>\n" +
" <node id=\"1682876100\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2156190\" lon=\"3.2180623\"/>\n" +
" <node id=\"1682876102\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2157232\" lon=\"3.2179435\"/>\n" +
" <node id=\"1682877254\" visible=\"true\" version=\"3\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2154254\" lon=\"3.2179953\"/>\n" +
" <node id=\"1682877255\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2155716\" lon=\"3.2180819\"/>\n" +
" <node id=\"1682877256\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2155250\" lon=\"3.2179243\"/>\n" +
" <node id=\"1682877257\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2154821\" lon=\"3.2181863\"/>\n" +
" <node id=\"1682879309\" visible=\"true\" version=\"2\" changeset=\"50435292\" timestamp=\"2017-07-20T15:06:25Z\" user=\"catweazle67\" uid=\"1976209\" lat=\"51.2154292\" lon=\"3.2182116\"/>\n" +
" </osm>"
const url = "https://www.openstreetmap.org/api/0.6/map?bbox=3.217620849609375,51.21548639922819,3.218994140625,51.21634661126673"
Utils.downloadJson(url).then(data =>{
const osmSource = {
rawDataHandlers : []
}
new FullNodeDatabaseSource(osmSource)
osmSource.rawDataHandlers[0]( data, 0)
})
import ShowDataLayer from "./UI/ShowDataLayer/ShowDataLayer";
import AllKnownLayers from "./Customizations/AllKnownLayers";
import Minimap from "./UI/Base/Minimap";
import StaticFeatureSource from "./Logic/FeatureSource/Sources/StaticFeatureSource";
import MinimapImplementation from "./UI/Base/MinimapImplementation";
import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers";
import BaseLayer from "./Models/BaseLayer";
import {UIEventSource} from "./Logic/UIEventSource";
import AvailableBaseLayersImplementation from "./Logic/Actors/AvailableBaseLayersImplementation";
MinimapImplementation.initialize()
AvailableBaseLayers.implement(new AvailableBaseLayersImplementation())
const confirmationMap = Minimap.createMiniMap({
background: new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto)
})
const features = [{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":1728823483},"geometry":{"type":"LineString","coordinates":[[3.216693,51.2147409],[3.2166930000000225,51.214740500000055]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":1728823481},"geometry":{"type":"LineString","coordinates":[[3.2167247,51.2146969],[3.21671060000004,51.2147159000002]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":1728823481},"geometry":{"type":"LineString","coordinates":[[3.2167247,51.2146969],[3.2167241999999976,51.214696799999714]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":1728823549},"geometry":{"type":"LineString","coordinates":[[3.2168871,51.2147399],[3.2168876999999547,51.21474009999989]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":4978289383},"geometry":{"type":"LineString","coordinates":[[3.2169973,51.2147676],[3.2169969000000034,51.21476780000005]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":4978289388},"geometry":{"type":"LineString","coordinates":[[3.2169829,51.2147884],[3.2169673999999895,51.21481170000002]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":4978289388},"geometry":{"type":"LineString","coordinates":[[3.2169829,51.2147884],[3.216949899999979,51.214808000000225]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":4978289388},"geometry":{"type":"LineString","coordinates":[[3.2169829,51.2147884],[3.2169306,51.21480400000028]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":4978289388},"geometry":{"type":"LineString","coordinates":[[3.2169829,51.2147884],[3.2169465999999756,51.214779199999825]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":4978288381},"geometry":{"type":"LineString","coordinates":[[3.2168856,51.2147638],[3.216885599999961,51.214763799999986]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":4978289386},"geometry":{"type":"LineString","coordinates":[[3.2168815,51.2147718],[3.216881100000038,51.21477160000009]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":4978289384},"geometry":{"type":"LineString","coordinates":[[3.2168674,51.2147683],[3.216867399999983,51.214768400000224]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":1728823514},"geometry":{"type":"LineString","coordinates":[[3.2168551,51.2147863],[3.2168551000000436,51.21478629999984]]}},"freshness":"2021-11-02T20:06:53.088Z"},{"feature":{"type":"Feature","properties":{"move":"yes","osm-id":1728823483},"geometry":{"type":"LineString","coordinates":[[3.216693,51.2147409],[3.2166930000000225,51.214740500000055]]}},"freshness":"2021-11-02T20:06:53.088Z"}]
const changePreview = new StaticFeatureSource(features.map(f => f.feature), false)
console.log("ChangePreview", changePreview.features.data)
new ShowDataLayer({
leafletMap: confirmationMap.leafletMap,
enablePopups: false,
zoomToFeatures: true,
features: changePreview,
layerToShow: AllKnownLayers.sharedLayers.get("conflation")
})
confirmationMap.SetStyle("height: 20rem").SetClass("w-full").AttachTo("maindiv")

View file

@ -0,0 +1,185 @@
import T from "./TestHelper";
import FullNodeDatabaseSource from "../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource";
import {Utils} from "../Utils";
export default class ReplaceGeometrySpec extends T {
constructor() {
super("ReplaceGeometry", [
["Simple house replacement", async () => {
const coordinates = <[number, number][]>[[
3.216690793633461,
51.21474084112525
],
[
3.2167256623506546,
51.214696737309964
],
[
3.2169999182224274,
51.214768983537674
],
[
3.2169650495052338,
51.21480720678671
],
[
3.2169368863105774,
51.21480090625335
],
[
3.2169489562511444,
51.21478074454077
],
[
3.216886594891548,
51.214765203214625
],
[
3.2168812304735184,
51.21477192378873
],
[
3.2168644666671753,
51.214768983537674
],
[
3.2168537378311157,
51.21478746511261
],
[
3.216690793633461,
51.21474084112525
]
]
Utils.injectJsonDownloadForTests(
"https://www.openstreetmap.org/api/0.6/way/160909312/full",
{
"version": "0.6",
"generator": "CGImap 0.8.5 (920083 spike-06.openstreetmap.org)",
"copyright": "OpenStreetMap and contributors",
"attribution": "http://www.openstreetmap.org/copyright",
"license": "http://opendatacommons.org/licenses/odbl/1-0/",
"elements": [{
"type": "node",
"id": 1728823481,
"lat": 51.2146969,
"lon": 3.2167247,
"timestamp": "2017-07-18T22:52:45Z",
"version": 2,
"changeset": 50391526,
"user": "catweazle67",
"uid": 1976209
}, {
"type": "node",
"id": 1728823483,
"lat": 51.2147409,
"lon": 3.216693,
"timestamp": "2017-07-18T22:52:45Z",
"version": 2,
"changeset": 50391526,
"user": "catweazle67",
"uid": 1976209
}, {
"type": "node",
"id": 1728823514,
"lat": 51.2147863,
"lon": 3.2168551,
"timestamp": "2017-07-18T22:52:45Z",
"version": 2,
"changeset": 50391526,
"user": "catweazle67",
"uid": 1976209
}, {
"type": "node",
"id": 1728823549,
"lat": 51.2147399,
"lon": 3.2168871,
"timestamp": "2017-07-18T22:52:46Z",
"version": 2,
"changeset": 50391526,
"user": "catweazle67",
"uid": 1976209
}, {
"type": "node",
"id": 4978288381,
"lat": 51.2147638,
"lon": 3.2168856,
"timestamp": "2017-07-18T22:52:21Z",
"version": 1,
"changeset": 50391526,
"user": "catweazle67",
"uid": 1976209
}, {
"type": "node",
"id": 4978289383,
"lat": 51.2147676,
"lon": 3.2169973,
"timestamp": "2017-07-18T22:52:21Z",
"version": 1,
"changeset": 50391526,
"user": "catweazle67",
"uid": 1976209
}, {
"type": "node",
"id": 4978289384,
"lat": 51.2147683,
"lon": 3.2168674,
"timestamp": "2017-07-18T22:52:21Z",
"version": 1,
"changeset": 50391526,
"user": "catweazle67",
"uid": 1976209
}, {
"type": "node",
"id": 4978289386,
"lat": 51.2147718,
"lon": 3.2168815,
"timestamp": "2017-07-18T22:52:21Z",
"version": 1,
"changeset": 50391526,
"user": "catweazle67",
"uid": 1976209
}, {
"type": "node",
"id": 4978289388,
"lat": 51.2147884,
"lon": 3.2169829,
"timestamp": "2017-07-18T22:52:21Z",
"version": 1,
"changeset": 50391526,
"user": "catweazle67",
"uid": 1976209
}, {
"type": "way",
"id": 160909312,
"timestamp": "2017-07-18T22:52:30Z",
"version": 2,
"changeset": 50391526,
"user": "catweazle67",
"uid": 1976209,
"nodes": [1728823483, 1728823514, 4978289384, 4978289386, 4978288381, 4978289388, 4978289383, 1728823549, 1728823481, 1728823483],
"tags": {
"addr:city": "Brugge",
"addr:country": "BE",
"addr:housenumber": "108",
"addr:postcode": "8000",
"addr:street": "Ezelstraat",
"building": "yes"
}
}]
}
)
const wayId = "way/160909312"
const url = `https://www.openstreetmap.org/api/0.6/${wayId}/full`;
const rawData = await Utils.downloadJsonCached(url, 1000)
}]
]);
}
}

View file

@ -13,6 +13,7 @@ import TileFreshnessCalculatorSpec from "./TileFreshnessCalculator.spec";
import WikidataSpecTest from "./Wikidata.spec.test";
import ImageProviderSpec from "./ImageProvider.spec";
import ActorsSpec from "./Actors.spec";
import ReplaceGeometrySpec from "./ReplaceGeometry.spec";
ScriptUtils.fixUtils()
@ -29,7 +30,8 @@ const allTests = [
new TileFreshnessCalculatorSpec(),
new WikidataSpecTest(),
new ImageProviderSpec(),
new ActorsSpec()
new ActorsSpec(),
new ReplaceGeometrySpec()
]
Utils.externalDownloadFunction = async (url) => {