Refactoring of GPS-location (uses featureSource too now), factoring out state, add ReplaceGeometryAction and conflation example
This commit is contained in:
parent
1db54f3c8e
commit
2484848cd6
37 changed files with 1035 additions and 467 deletions
|
@ -1,13 +1,16 @@
|
||||||
import * as L from "leaflet";
|
|
||||||
import {UIEventSource} from "../UIEventSource";
|
import {UIEventSource} from "../UIEventSource";
|
||||||
import Svg from "../../Svg";
|
import Svg from "../../Svg";
|
||||||
import Img from "../../UI/Base/Img";
|
|
||||||
import {LocalStorageSource} from "../Web/LocalStorageSource";
|
import {LocalStorageSource} from "../Web/LocalStorageSource";
|
||||||
import {VariableUiElement} from "../../UI/Base/VariableUIElement";
|
import {VariableUiElement} from "../../UI/Base/VariableUIElement";
|
||||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
||||||
import {QueryParameters} from "../Web/QueryParameters";
|
import {QueryParameters} from "../Web/QueryParameters";
|
||||||
|
import FeatureSource from "../FeatureSource/FeatureSource";
|
||||||
|
import StaticFeatureSource from "../FeatureSource/Sources/StaticFeatureSource";
|
||||||
|
|
||||||
export default class GeoLocationHandler extends VariableUiElement {
|
export default class GeoLocationHandler extends VariableUiElement {
|
||||||
|
|
||||||
|
public readonly currentLocation : FeatureSource
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wether or not the geolocation is active, aka the user requested the current location
|
* Wether or not the geolocation is active, aka the user requested the current location
|
||||||
* @private
|
* @private
|
||||||
|
@ -25,20 +28,12 @@ export default class GeoLocationHandler extends VariableUiElement {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private readonly _permission: UIEventSource<string>;
|
private readonly _permission: UIEventSource<string>;
|
||||||
/***
|
|
||||||
* The marker on the map, in order to update it
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private _marker: L.Marker;
|
|
||||||
/**
|
/**
|
||||||
* Literally: _currentGPSLocation.data != undefined
|
* Literally: _currentGPSLocation.data != undefined
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private readonly _hasLocation: UIEventSource<boolean>;
|
private readonly _hasLocation: UIEventSource<boolean>;
|
||||||
private readonly _currentGPSLocation: UIEventSource<{
|
private readonly _currentGPSLocation: UIEventSource<Coordinates>;
|
||||||
latlng: any;
|
|
||||||
accuracy: number;
|
|
||||||
}>;
|
|
||||||
/**
|
/**
|
||||||
* Kept in order to update the marker
|
* Kept in order to update the marker
|
||||||
* @private
|
* @private
|
||||||
|
@ -63,8 +58,8 @@ export default class GeoLocationHandler extends VariableUiElement {
|
||||||
private readonly _layoutToUse: LayoutConfig;
|
private readonly _layoutToUse: LayoutConfig;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
currentGPSLocation: UIEventSource<{ latlng: any; accuracy: number }>,
|
currentGPSLocation: UIEventSource<Coordinates>,
|
||||||
leafletMap: UIEventSource<L.Map>,
|
leafletMap: UIEventSource<any>,
|
||||||
layoutToUse: LayoutConfig
|
layoutToUse: LayoutConfig
|
||||||
) {
|
) {
|
||||||
const hasLocation = currentGPSLocation.map(
|
const hasLocation = currentGPSLocation.map(
|
||||||
|
@ -182,10 +177,25 @@ export default class GeoLocationHandler extends VariableUiElement {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.currentLocation = new StaticFeatureSource([], false)
|
||||||
this._currentGPSLocation.addCallback((location) => {
|
this._currentGPSLocation.addCallback((location) => {
|
||||||
self._previousLocationGrant.setData("granted");
|
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 =
|
const timeSinceRequest =
|
||||||
(new Date().getTime() - (self._lastUserRequest?.getTime() ?? 0)) / 1000;
|
(new Date().getTime() - (self._lastUserRequest?.getTime() ?? 0)) / 1000;
|
||||||
if (timeSinceRequest < 30) {
|
if (timeSinceRequest < 30) {
|
||||||
|
@ -194,33 +204,8 @@ export default class GeoLocationHandler extends VariableUiElement {
|
||||||
self.MoveToCurrentLoction();
|
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) {
|
private init(askPermission: boolean, forceZoom: boolean) {
|
||||||
|
@ -261,8 +246,8 @@ export default class GeoLocationHandler extends VariableUiElement {
|
||||||
this._lastUserRequest = undefined;
|
this._lastUserRequest = undefined;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this._currentGPSLocation.data.latlng[0] === 0 &&
|
this._currentGPSLocation.data.latitude === 0 &&
|
||||||
this._currentGPSLocation.data.latlng[1] === 0
|
this._currentGPSLocation.data.longitude === 0
|
||||||
) {
|
) {
|
||||||
console.debug("Not moving to GPS-location: it is null island");
|
console.debug("Not moving to GPS-location: it is null island");
|
||||||
return;
|
return;
|
||||||
|
@ -275,20 +260,20 @@ export default class GeoLocationHandler extends VariableUiElement {
|
||||||
if (b !== true) {
|
if (b !== true) {
|
||||||
// B is an array with our locklocation
|
// B is an array with our locklocation
|
||||||
inRange =
|
inRange =
|
||||||
b[0][0] <= location.latlng[0] &&
|
b[0][0] <= location.latitude &&
|
||||||
location.latlng[0] <= b[1][0] &&
|
location.latitude <= b[1][0] &&
|
||||||
b[0][1] <= location.latlng[1] &&
|
b[0][1] <= location.longitude &&
|
||||||
location.latlng[1] <= b[1][1];
|
location.longitude <= b[1][1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!inRange) {
|
if (!inRange) {
|
||||||
console.log(
|
console.log(
|
||||||
"Not zooming to GPS location: out of bounds",
|
"Not zooming to GPS location: out of bounds",
|
||||||
b,
|
b,
|
||||||
location.latlng
|
location
|
||||||
);
|
);
|
||||||
} else {
|
} 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(
|
navigator.geolocation.watchPosition(
|
||||||
function (position) {
|
function (position) {
|
||||||
self._currentGPSLocation.setData({
|
self._currentGPSLocation.setData(position.coords);
|
||||||
latlng: [position.coords.latitude, position.coords.longitude],
|
|
||||||
accuracy: position.coords.accuracy,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
function () {
|
function () {
|
||||||
console.warn("Could not get location with navigator.geolocation");
|
console.warn("Could not get location with navigator.geolocation");
|
||||||
|
|
|
@ -117,6 +117,11 @@ export class BBox {
|
||||||
return this.minLat
|
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 {
|
pad(factor: number, maxIncrease = 2): BBox {
|
||||||
|
|
||||||
const latDiff = Math.min(maxIncrease / 2, Math.abs(this.maxLat - this.minLat) * factor)
|
const latDiff = Math.min(maxIncrease / 2, Math.abs(this.maxLat - this.minLat) * factor)
|
||||||
|
|
|
@ -228,11 +228,15 @@ export default class FeaturePipeline {
|
||||||
})
|
})
|
||||||
|
|
||||||
if(state.layoutToUse.trackAllNodes){
|
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)
|
new RegisteringAllFromFeatureSourceActor(tile)
|
||||||
perLayerHierarchy.get(tile.layer.layerDef.id).registerTile(tile)
|
perLayerHierarchy.get(tile.layer.layerDef.id).registerTile(tile)
|
||||||
tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile))
|
tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
osmFeatureSource.rawDataHandlers.push((osmJson, tileId) => fullNodeDb.handleOsmJson(osmJson, tileId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource {
|
||||||
const w = new OsmWay(change.id)
|
const w = new OsmWay(change.id)
|
||||||
w.tags = tags
|
w.tags = tags
|
||||||
w.nodes = change.changes["nodes"]
|
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())
|
add(w.asGeoJson())
|
||||||
break;
|
break;
|
||||||
case "relation":
|
case "relation":
|
||||||
|
|
|
@ -46,8 +46,6 @@ export default class RenderingMultiPlexerFeatureSource {
|
||||||
|
|
||||||
for (const f of features) {
|
for (const f of features) {
|
||||||
const feat = f.feature;
|
const feat = f.feature;
|
||||||
|
|
||||||
|
|
||||||
if (feat.geometry.type === "Point") {
|
if (feat.geometry.type === "Point") {
|
||||||
|
|
||||||
for (const rendering of pointRenderings) {
|
for (const rendering of pointRenderings) {
|
||||||
|
|
|
@ -2,30 +2,103 @@ import TileHierarchy from "./TileHierarchy";
|
||||||
import FeatureSource, {FeatureSourceForLayer, Tiled} from "../FeatureSource";
|
import FeatureSource, {FeatureSourceForLayer, Tiled} from "../FeatureSource";
|
||||||
import {OsmNode, OsmObject, OsmWay} from "../../Osm/OsmObject";
|
import {OsmNode, OsmObject, OsmWay} from "../../Osm/OsmObject";
|
||||||
import SimpleFeatureSource from "../Sources/SimpleFeatureSource";
|
import SimpleFeatureSource from "../Sources/SimpleFeatureSource";
|
||||||
import {UIEventSource} from "../../UIEventSource";
|
|
||||||
import FilteredLayer from "../../../Models/FilteredLayer";
|
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> {
|
export default class FullNodeDatabaseSource implements TileHierarchy<FeatureSource & Tiled> {
|
||||||
public readonly loadedTiles = new Map<number, FeatureSource & Tiled>()
|
public readonly loadedTiles = new Map<number, FeatureSource & Tiled>()
|
||||||
private readonly onTileLoaded: (tile: (Tiled & FeatureSourceForLayer)) => void;
|
private readonly onTileLoaded: (tile: (Tiled & FeatureSourceForLayer)) => void;
|
||||||
private readonly layer : FilteredLayer
|
private readonly layer: FilteredLayer
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
state: {
|
layer: FilteredLayer,
|
||||||
readonly filteredLayers: UIEventSource<FilteredLayer[]>},
|
|
||||||
osmFeatureSource: { rawDataHandlers: ((data: any, tileId: number) => void)[] },
|
|
||||||
onTileLoaded: ((tile: Tiled & FeatureSourceForLayer) => void)) {
|
onTileLoaded: ((tile: Tiled & FeatureSourceForLayer) => void)) {
|
||||||
this.onTileLoaded = onTileLoaded
|
this.onTileLoaded = onTileLoaded
|
||||||
this.layer = state.filteredLayers.data.filter(l => l.layerDef.id === "type_node")[0]
|
this.layer = layer;
|
||||||
if(this.layer === undefined){
|
if (this.layer === undefined) {
|
||||||
throw "Weird: tracking all nodes, but layer 'type_node' is not defined"
|
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 allObjects = OsmObject.ParseObjects(osmJson.elements)
|
||||||
const nodesById = new Map<number, OsmNode>()
|
const nodesById = new Map<number, OsmNode>()
|
||||||
|
@ -57,7 +130,7 @@ export default class FullNodeDatabaseSource implements TileHierarchy<FeatureSour
|
||||||
})
|
})
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
const asGeojsonFeatures = Array.from(nodesById.values()).map(osmNode => ({
|
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)
|
const featureSource = new SimpleFeatureSource(this.layer, tileId)
|
||||||
|
@ -67,4 +140,11 @@ export default class FullNodeDatabaseSource implements TileHierarchy<FeatureSour
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConflationConfig {
|
||||||
|
withinRangeOfM: number,
|
||||||
|
ifMatches: TagsFilter,
|
||||||
|
mode: "reuse_osm_point" | "move_osm_point"
|
||||||
}
|
}
|
|
@ -68,7 +68,7 @@ export default class OsmFeatureSource {
|
||||||
console.log("Tile download", Tiles.tile_from_index(neededTile).join("/"), "started")
|
console.log("Tile download", Tiles.tile_from_index(neededTile).join("/"), "started")
|
||||||
self.downloadedTiles.add(neededTile)
|
self.downloadedTiles.add(neededTile)
|
||||||
self.LoadTile(...Tiles.tile_from_index(neededTile)).then(_ => {
|
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) {
|
} catch (e) {
|
||||||
|
@ -98,7 +98,7 @@ export default class OsmFeatureSource {
|
||||||
console.log("Attempting to get tile", z, x, y, "from the osm api")
|
console.log("Attempting to get tile", z, x, y, "from the osm api")
|
||||||
const osmJson = await Utils.downloadJson(url)
|
const osmJson = await Utils.downloadJson(url)
|
||||||
try {
|
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)))
|
this.rawDataHandlers.forEach(handler => handler(osmJson, Tiles.tile_index(z, x, y)))
|
||||||
const geojson = OsmToGeoJson.default(osmJson,
|
const geojson = OsmToGeoJson.default(osmJson,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -110,10 +110,8 @@ export default class OsmFeatureSource {
|
||||||
// We only keep what is needed
|
// We only keep what is needed
|
||||||
|
|
||||||
geojson.features = geojson.features.filter(feature => this.allowedTags.matchesProperties(feature.properties))
|
geojson.features = geojson.features.filter(feature => this.allowedTags.matchesProperties(feature.properties))
|
||||||
|
|
||||||
geojson.features.forEach(f => f.properties["_backend"] = this._backend)
|
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);
|
const index = Tiles.tile_index(z, x, y);
|
||||||
new PerLayerFeatureSourceSplitter(this.filteredLayers,
|
new PerLayerFeatureSourceSplitter(this.filteredLayers,
|
||||||
this.handleTile,
|
this.handleTile,
|
||||||
|
|
|
@ -11,7 +11,7 @@ export default class ChangeTagAction extends OsmChangeAction {
|
||||||
|
|
||||||
constructor(elementId: string, tagsFilter: TagsFilter, currentTags: any, meta: {
|
constructor(elementId: string, tagsFilter: TagsFilter, currentTags: any, meta: {
|
||||||
theme: string,
|
theme: string,
|
||||||
changeType: "answer" | "soft-delete" | "add-image"
|
changeType: "answer" | "soft-delete" | "add-image" | string
|
||||||
}) {
|
}) {
|
||||||
super();
|
super();
|
||||||
this._elementId = elementId;
|
this._elementId = elementId;
|
||||||
|
@ -27,11 +27,16 @@ export default class ChangeTagAction extends OsmChangeAction {
|
||||||
const key = kv.k;
|
const key = kv.k;
|
||||||
const value = kv.v;
|
const value = kv.v;
|
||||||
if (key === undefined || key === null) {
|
if (key === undefined || key === null) {
|
||||||
console.log("Invalid key");
|
console.error("Invalid key:", key);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (value === undefined || value === null) {
|
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;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,39 +4,25 @@ import {Changes} from "../Changes";
|
||||||
import {Tag} from "../../Tags/Tag";
|
import {Tag} from "../../Tags/Tag";
|
||||||
import CreateNewNodeAction from "./CreateNewNodeAction";
|
import CreateNewNodeAction from "./CreateNewNodeAction";
|
||||||
import {And} from "../../Tags/And";
|
import {And} from "../../Tags/And";
|
||||||
import {TagsFilter} from "../../Tags/TagsFilter";
|
|
||||||
|
|
||||||
export default class CreateNewWayAction extends OsmChangeAction {
|
export default class CreateNewWayAction extends OsmChangeAction {
|
||||||
public newElementId: string = undefined
|
public newElementId: string = undefined
|
||||||
private readonly coordinates: ({ nodeId?: number, lat: number, lon: number })[];
|
private readonly coordinates: ({ nodeId?: number, lat: number, lon: number })[];
|
||||||
private readonly tags: Tag[];
|
private readonly tags: Tag[];
|
||||||
private readonly _options: {
|
private readonly _options: {
|
||||||
theme: string, existingPointHandling?: {
|
theme: string
|
||||||
withinRangeOfM: number,
|
|
||||||
ifMatches?: TagsFilter,
|
|
||||||
mode: "reuse_osm_point" | "move_osm_point"
|
|
||||||
} []
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Creates a new way to upload to OSM
|
* 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 coordinates: the coordinates. Might have a nodeId, in this case, this node will be used
|
||||||
* @param options
|
* @param options
|
||||||
*/
|
*/
|
||||||
constructor(tags: Tag[], coordinates: ({ nodeId?: number, lat: number, lon: number })[],
|
constructor(tags: Tag[], coordinates: ({ nodeId?: number, lat: number, lon: number })[],
|
||||||
options: {
|
options: {
|
||||||
theme: string,
|
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"
|
|
||||||
} []
|
|
||||||
}) {
|
}) {
|
||||||
super()
|
super()
|
||||||
this.coordinates = coordinates;
|
this.coordinates = coordinates;
|
||||||
|
|
232
Logic/Osm/Actions/ReplaceGeometryAction.ts
Normal file
232
Logic/Osm/Actions/ReplaceGeometryAction.ts
Normal 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};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -114,7 +114,16 @@ export class Changes {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async applyAction(action: OsmChangeAction): Promise<void> {
|
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)
|
console.log("Received changes:", changes)
|
||||||
this.pendingChanges.data.push(...changes);
|
this.pendingChanges.data.push(...changes);
|
||||||
this.pendingChanges.ping();
|
this.pendingChanges.ping();
|
||||||
|
@ -126,6 +135,7 @@ export class Changes {
|
||||||
CreateNewNodeAction.registerIdRewrites(mappings)
|
CreateNewNodeAction.registerIdRewrites(mappings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UPload the selected changes to OSM.
|
* UPload the selected changes to OSM.
|
||||||
* Returns 'true' if successfull and if they can be removed
|
* Returns 'true' if successfull and if they can be removed
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {QueryParameters} from "../Web/QueryParameters";
|
||||||
import * as personal from "../../assets/themes/personal/personal.json";
|
import * as personal from "../../assets/themes/personal/personal.json";
|
||||||
import FilterConfig from "../../Models/ThemeConfig/FilterConfig";
|
import FilterConfig from "../../Models/ThemeConfig/FilterConfig";
|
||||||
import ShowOverlayLayer from "../../UI/ShowDataLayer/ShowOverlayLayer";
|
import ShowOverlayLayer from "../../UI/ShowDataLayer/ShowOverlayLayer";
|
||||||
|
import {Coord} from "@turf/turf";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains all the leaflet-map related state
|
* Contains all the leaflet-map related state
|
||||||
|
@ -44,13 +45,7 @@ export default class MapState extends UserRelatedState {
|
||||||
/**
|
/**
|
||||||
* The location as delivered by the GPS
|
* The location as delivered by the GPS
|
||||||
*/
|
*/
|
||||||
public currentGPSLocation: UIEventSource<{
|
public currentGPSLocation: UIEventSource<Coordinates> = new UIEventSource<Coordinates>(undefined);
|
||||||
latlng: { lat: number; lng: number };
|
|
||||||
accuracy: number;
|
|
||||||
}> = new UIEventSource<{
|
|
||||||
latlng: { lat: number; lng: number };
|
|
||||||
accuracy: number;
|
|
||||||
}>(undefined);
|
|
||||||
|
|
||||||
public readonly mainMapObject: BaseUIElement & MinimapObj;
|
public readonly mainMapObject: BaseUIElement & MinimapObj;
|
||||||
|
|
||||||
|
|
28
UI/Base/AsyncLazy.ts
Normal file
28
UI/Base/AsyncLazy.ts
Normal 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ export interface MinimapOptions {
|
||||||
export interface MinimapObj {
|
export interface MinimapObj {
|
||||||
readonly leafletMap: UIEventSource<any>,
|
readonly leafletMap: UIEventSource<any>,
|
||||||
installBounds(factor: number | BBox, showRange?: boolean) : void
|
installBounds(factor: number | BBox, showRange?: boolean) : void
|
||||||
|
TakeScreenshot(): Promise<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Minimap {
|
export default class Minimap {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {Map} from "leaflet";
|
||||||
import Minimap, {MinimapObj, MinimapOptions} from "./Minimap";
|
import Minimap, {MinimapObj, MinimapOptions} from "./Minimap";
|
||||||
import {BBox} from "../../Logic/BBox";
|
import {BBox} from "../../Logic/BBox";
|
||||||
import 'leaflet-polylineoffset'
|
import 'leaflet-polylineoffset'
|
||||||
|
import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter";
|
||||||
|
|
||||||
export default class MinimapImplementation extends BaseUIElement implements MinimapObj {
|
export default class MinimapImplementation extends BaseUIElement implements MinimapObj {
|
||||||
private static _nextId = 0;
|
private static _nextId = 0;
|
||||||
|
@ -278,4 +279,10 @@ export default class MinimapImplementation extends BaseUIElement implements Mini
|
||||||
|
|
||||||
this.leafletMap.setData(map)
|
this.leafletMap.setData(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async TakeScreenshot(){
|
||||||
|
const screenshotter = new SimpleMapScreenshoter();
|
||||||
|
screenshotter.addTo(this.leafletMap.data);
|
||||||
|
return await screenshotter.takeScreen('image')
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -14,6 +14,9 @@ import Toggle from "../Input/Toggle";
|
||||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
||||||
import {Utils} from "../../Utils";
|
import {Utils} from "../../Utils";
|
||||||
import UserRelatedState from "../../Logic/State/UserRelatedState";
|
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 {
|
export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
|
||||||
|
|
||||||
|
@ -24,7 +27,10 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
|
||||||
layoutToUse: LayoutConfig,
|
layoutToUse: LayoutConfig,
|
||||||
osmConnection: OsmConnection,
|
osmConnection: OsmConnection,
|
||||||
featureSwitchShareScreen: UIEventSource<boolean>,
|
featureSwitchShareScreen: UIEventSource<boolean>,
|
||||||
featureSwitchMoreQuests: UIEventSource<boolean>
|
featureSwitchMoreQuests: UIEventSource<boolean>,
|
||||||
|
locationControl: UIEventSource<Loc>,
|
||||||
|
backgroundLayer: UIEventSource<BaseLayer>,
|
||||||
|
filteredLayers: UIEventSource<FilteredLayer[]>
|
||||||
} & UserRelatedState) {
|
} & UserRelatedState) {
|
||||||
const layoutToUse = state.layoutToUse;
|
const layoutToUse = state.layoutToUse;
|
||||||
super(
|
super(
|
||||||
|
@ -39,7 +45,8 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
|
||||||
layoutToUse: LayoutConfig,
|
layoutToUse: LayoutConfig,
|
||||||
osmConnection: OsmConnection,
|
osmConnection: OsmConnection,
|
||||||
featureSwitchShareScreen: UIEventSource<boolean>,
|
featureSwitchShareScreen: UIEventSource<boolean>,
|
||||||
featureSwitchMoreQuests: UIEventSource<boolean>
|
featureSwitchMoreQuests: UIEventSource<boolean>,
|
||||||
|
locationControl: UIEventSource<Loc>, backgroundLayer: UIEventSource<BaseLayer>, filteredLayers: UIEventSource<FilteredLayer[]>
|
||||||
} & UserRelatedState,
|
} & UserRelatedState,
|
||||||
isShown: UIEventSource<boolean>):
|
isShown: UIEventSource<boolean>):
|
||||||
{ header: string | BaseUIElement; content: BaseUIElement }[] {
|
{ header: string | BaseUIElement; content: BaseUIElement }[] {
|
||||||
|
@ -77,7 +84,8 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
|
||||||
layoutToUse: LayoutConfig,
|
layoutToUse: LayoutConfig,
|
||||||
osmConnection: OsmConnection,
|
osmConnection: OsmConnection,
|
||||||
featureSwitchShareScreen: UIEventSource<boolean>,
|
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>) {
|
} & UserRelatedState, currentTab: UIEventSource<number>, isShown: UIEventSource<boolean>) {
|
||||||
|
|
||||||
const tabs = FullWelcomePaneWithTabs.ConstructBaseTabs(state, isShown)
|
const tabs = FullWelcomePaneWithTabs.ConstructBaseTabs(state, isShown)
|
||||||
|
|
|
@ -9,7 +9,6 @@ import Toggle from "../Input/Toggle";
|
||||||
import CreateNewNodeAction from "../../Logic/Osm/Actions/CreateNewNodeAction";
|
import CreateNewNodeAction from "../../Logic/Osm/Actions/CreateNewNodeAction";
|
||||||
import {Tag} from "../../Logic/Tags/Tag";
|
import {Tag} from "../../Logic/Tags/Tag";
|
||||||
import Loading from "../Base/Loading";
|
import Loading from "../Base/Loading";
|
||||||
import OsmChangeAction from "../../Logic/Osm/Actions/OsmChangeAction";
|
|
||||||
import CreateNewWayAction from "../../Logic/Osm/Actions/CreateNewWayAction";
|
import CreateNewWayAction from "../../Logic/Osm/Actions/CreateNewWayAction";
|
||||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
||||||
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
|
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
|
||||||
|
@ -26,6 +25,13 @@ import SpecialVisualizations, {SpecialVisualization} from "../SpecialVisualizati
|
||||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||||
import Svg from "../../Svg";
|
import Svg from "../../Svg";
|
||||||
import {Utils} from "../../Utils";
|
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 {
|
export interface ImportButtonState {
|
||||||
|
@ -38,6 +44,8 @@ export interface ImportButtonState {
|
||||||
feature: any,
|
feature: any,
|
||||||
minZoom: number,
|
minZoom: number,
|
||||||
state: {
|
state: {
|
||||||
|
backgroundLayer: UIEventSource<BaseLayer>;
|
||||||
|
filteredLayers: UIEventSource<FilteredLayer[]>;
|
||||||
featureSwitchUserbadge: UIEventSource<boolean>;
|
featureSwitchUserbadge: UIEventSource<boolean>;
|
||||||
featurePipeline: FeaturePipeline;
|
featurePipeline: FeaturePipeline;
|
||||||
allElements: ElementStorage;
|
allElements: ElementStorage;
|
||||||
|
@ -48,8 +56,14 @@ export interface ImportButtonState {
|
||||||
locationControl: UIEventSource<{ zoom: number }>
|
locationControl: UIEventSource<{ zoom: number }>
|
||||||
},
|
},
|
||||||
guiState: { filterViewIsOpened: UIEventSource<boolean> },
|
guiState: { filterViewIsOpened: UIEventSource<boolean> },
|
||||||
snapToLayers?: string[],
|
|
||||||
|
snapSettings?: {
|
||||||
|
snapToLayers: string[],
|
||||||
snapToLayersMaxDist?: number
|
snapToLayersMaxDist?: number
|
||||||
|
},
|
||||||
|
conflationSettings?: {
|
||||||
|
conflateWayId: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ImportButtonSpecialViz implements SpecialVisualization {
|
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
|
#### 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}
|
${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",
|
doc: "How far the contributor must zoom in before being able to import the point",
|
||||||
defaultValue: "18"
|
defaultValue: "18"
|
||||||
}, {
|
}, {
|
||||||
name: "Snap onto layer(s)",
|
name: "Snap onto layer(s)/replace geometry with this other way",
|
||||||
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",
|
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",
|
name: "snap max distance",
|
||||||
doc: "The maximum distance that this point will move to snap onto a layer (in meters)",
|
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 id = tagSource.data.id;
|
||||||
const feature = state.allElements.ContainingFeatures.get(id)
|
const feature = state.allElements.ContainingFeatures.get(id)
|
||||||
let minZoom = args[4] == "" ? 18 : Number(args[4])
|
let minZoom = args[4] == "" ? 18 : Number(args[4])
|
||||||
if(isNaN(minZoom)){
|
if (isNaN(minZoom)) {
|
||||||
console.warn("Invalid minzoom:", minZoom)
|
console.warn("Invalid minzoom:", minZoom)
|
||||||
minZoom = 18
|
minZoom = 18
|
||||||
}
|
}
|
||||||
|
@ -145,6 +160,17 @@ ${Utils.Special_visualizations_tagsToApplyHelpText}
|
||||||
img = () => Svg.add_ui()
|
img = () => Svg.add_ui()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 snapToLayers = args[5]?.split(";").filter(s => s !== "")
|
||||||
const snapToLayersMaxDist = Number(args[6] ?? 6)
|
const snapToLayersMaxDist = Number(args[6] ?? 6)
|
||||||
|
|
||||||
|
@ -153,6 +179,11 @@ ${Utils.Special_visualizations_tagsToApplyHelpText}
|
||||||
console.error(e)
|
console.error(e)
|
||||||
return new FixedUiElement(e).SetClass("alert")
|
return new FixedUiElement(e).SetClass("alert")
|
||||||
}
|
}
|
||||||
|
snapSettings = {
|
||||||
|
snapToLayers,
|
||||||
|
snapToLayersMaxDist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new ImportButton(
|
return new ImportButton(
|
||||||
{
|
{
|
||||||
|
@ -160,8 +191,8 @@ ${Utils.Special_visualizations_tagsToApplyHelpText}
|
||||||
feature, newTags, message, minZoom,
|
feature, newTags, message, minZoom,
|
||||||
originalTags: tagSource,
|
originalTags: tagSource,
|
||||||
targetLayer,
|
targetLayer,
|
||||||
snapToLayers,
|
snapSettings,
|
||||||
snapToLayersMaxDist
|
conflationSettings
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -201,7 +232,7 @@ export default class ImportButton extends Toggle {
|
||||||
|
|
||||||
const importClicked = new UIEventSource(false);
|
const importClicked = new UIEventSource(false);
|
||||||
const importFlow = new Toggle(
|
const importFlow = new Toggle(
|
||||||
new Lazy(() => ImportButton.createConfirmPanel(o, isImported, importClicked)),
|
ImportButton.createConfirmPanel(o, isImported, importClicked),
|
||||||
importButton,
|
importButton,
|
||||||
importClicked
|
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,
|
o: ImportButtonState,
|
||||||
isImported: UIEventSource<boolean>,
|
isImported: UIEventSource<boolean>,
|
||||||
importClicked: UIEventSource<boolean>): BaseUIElement {
|
importClicked: UIEventSource<boolean>): BaseUIElement {
|
||||||
|
@ -239,21 +384,24 @@ export default class ImportButton extends Toggle {
|
||||||
}
|
}
|
||||||
o.originalTags.data["_imported"] = "yes"
|
o.originalTags.data["_imported"] = "yes"
|
||||||
o.originalTags.ping() // will set isImported as per its definition
|
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)
|
await o.state.changes.applyAction(newElementAction)
|
||||||
o.state.selectedElement.setData(o.state.allElements.ContainingFeatures.get(
|
o.state.selectedElement.setData(o.state.allElements.ContainingFeatures.get(
|
||||||
newElementAction.newElementId
|
newElementAction.newElementId
|
||||||
))
|
))
|
||||||
console.log("Did set selected element to", o.state.allElements.ContainingFeatures.get(
|
|
||||||
newElementAction.newElementId
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
importClicked.setData(false)
|
importClicked.setData(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (o.feature.geometry.type === "Point") {
|
|
||||||
const presetInfo = <PresetInfo>{
|
const presetInfo = <PresetInfo>{
|
||||||
tags: o.newTags.data,
|
tags: o.newTags.data,
|
||||||
icon: o.image,
|
icon: o.image,
|
||||||
|
@ -261,17 +409,18 @@ export default class ImportButton extends Toggle {
|
||||||
layerToAddTo: o.targetLayer,
|
layerToAddTo: o.targetLayer,
|
||||||
name: o.message,
|
name: o.message,
|
||||||
title: o.message,
|
title: o.message,
|
||||||
preciseInput: { snapToLayers: o.snapToLayers,
|
preciseInput: {
|
||||||
maxSnapDistance: o.snapToLayersMaxDist}
|
snapToLayers: o.snapSettings?.snapToLayers,
|
||||||
|
maxSnapDistance: o.snapSettings?.snapToLayersMaxDist
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const [lon, lat] = o.feature.geometry.coordinates
|
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), {
|
return new ConfirmLocationOfPoint(o.state, o.guiState.filterViewIsOpened, presetInfo, Translations.W(o.message), {
|
||||||
lon,
|
lon,
|
||||||
lat
|
lat
|
||||||
}, confirm, cancel)
|
}, confirm, cancel)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -279,41 +428,4 @@ export default class ImportButton extends Toggle {
|
||||||
const type = feature.geometry.type
|
const type = feature.geometry.type
|
||||||
return type === "Point" || type === "LineString" || (type === "Polygon" && feature.geometry.coordinates.length === 1)
|
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;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -14,6 +14,8 @@ import Loc from "../../Models/Loc";
|
||||||
import {BBox} from "../../Logic/BBox";
|
import {BBox} from "../../Logic/BBox";
|
||||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
||||||
import FilteredLayer from "../../Models/FilteredLayer";
|
import FilteredLayer from "../../Models/FilteredLayer";
|
||||||
|
import BaseLayer from "../../Models/BaseLayer";
|
||||||
|
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
|
||||||
|
|
||||||
export default class LeftControls extends Combine {
|
export default class LeftControls extends Combine {
|
||||||
|
|
||||||
|
@ -26,7 +28,9 @@ export default class LeftControls extends Combine {
|
||||||
featureSwitchEnableExport: UIEventSource<boolean>,
|
featureSwitchEnableExport: UIEventSource<boolean>,
|
||||||
featureSwitchExportAsPdf: UIEventSource<boolean>,
|
featureSwitchExportAsPdf: UIEventSource<boolean>,
|
||||||
filteredLayers: UIEventSource<FilteredLayer[]>,
|
filteredLayers: UIEventSource<FilteredLayer[]>,
|
||||||
featureSwitchFilter: UIEventSource<boolean>
|
featureSwitchFilter: UIEventSource<boolean>,
|
||||||
|
backgroundLayer: UIEventSource<BaseLayer>,
|
||||||
|
osmConnection: OsmConnection
|
||||||
},
|
},
|
||||||
guiState: {
|
guiState: {
|
||||||
downloadControlIsOpened: UIEventSource<boolean>,
|
downloadControlIsOpened: UIEventSource<boolean>,
|
||||||
|
|
|
@ -4,17 +4,30 @@ import MapControlButton from "../MapControlButton";
|
||||||
import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler";
|
import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler";
|
||||||
import Svg from "../../Svg";
|
import Svg from "../../Svg";
|
||||||
import MapState from "../../Logic/State/MapState";
|
import MapState from "../../Logic/State/MapState";
|
||||||
|
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer";
|
||||||
|
import AllKnownLayers from "../../Customizations/AllKnownLayers";
|
||||||
|
|
||||||
export default class RightControls extends Combine {
|
export default class RightControls extends Combine {
|
||||||
|
|
||||||
constructor(state:MapState) {
|
constructor(state:MapState) {
|
||||||
const geolocationButton = new Toggle(
|
|
||||||
new MapControlButton(
|
const geolocatioHandler = new GeoLocationHandler(
|
||||||
new GeoLocationHandler(
|
|
||||||
state.currentGPSLocation,
|
state.currentGPSLocation,
|
||||||
state.leafletMap,
|
state.leafletMap,
|
||||||
state.layoutToUse
|
state.layoutToUse
|
||||||
), {
|
)
|
||||||
|
|
||||||
|
new ShowDataLayer({
|
||||||
|
layerToShow: AllKnownLayers.sharedLayers.get("gps_location"),
|
||||||
|
leafletMap: state.leafletMap,
|
||||||
|
enablePopups: true,
|
||||||
|
features: geolocatioHandler.currentLocation
|
||||||
|
})
|
||||||
|
|
||||||
|
const geolocationButton = new Toggle(
|
||||||
|
new MapControlButton(
|
||||||
|
geolocatioHandler
|
||||||
|
, {
|
||||||
dontStyle: true
|
dontStyle: true
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
|
@ -8,11 +8,14 @@ import Toggle from "../Input/Toggle";
|
||||||
import Translations from "../i18n/Translations";
|
import Translations from "../i18n/Translations";
|
||||||
import BaseUIElement from "../BaseUIElement";
|
import BaseUIElement from "../BaseUIElement";
|
||||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
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 {
|
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 layout = state?.layoutToUse;
|
||||||
const tr = Translations.t.general.sharescreen;
|
const tr = Translations.t.general.sharescreen;
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,6 @@ import FullWelcomePaneWithTabs from "./BigComponents/FullWelcomePaneWithTabs";
|
||||||
import MapControlButton from "./MapControlButton";
|
import MapControlButton from "./MapControlButton";
|
||||||
import Svg from "../Svg";
|
import Svg from "../Svg";
|
||||||
import Toggle from "./Input/Toggle";
|
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 UserBadge from "./BigComponents/UserBadge";
|
||||||
import SearchAndGo from "./BigComponents/SearchAndGo";
|
import SearchAndGo from "./BigComponents/SearchAndGo";
|
||||||
import Link from "./Base/Link";
|
import Link from "./Base/Link";
|
||||||
|
@ -24,77 +21,7 @@ import Translations from "./i18n/Translations";
|
||||||
import SimpleAddUI from "./BigComponents/SimpleAddUI";
|
import SimpleAddUI from "./BigComponents/SimpleAddUI";
|
||||||
import StrayClickHandler from "../Logic/Actors/StrayClickHandler";
|
import StrayClickHandler from "../Logic/Actors/StrayClickHandler";
|
||||||
import Lazy from "./Base/Lazy";
|
import Lazy from "./Base/Lazy";
|
||||||
|
import {DefaultGuiState} from "./DefaultGuiState";
|
||||||
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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
74
UI/DefaultGuiState.ts
Normal file
74
UI/DefaultGuiState.ts
Normal 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,6 @@
|
||||||
|
|
||||||
|
|
||||||
import jsPDF from "jspdf";
|
import jsPDF from "jspdf";
|
||||||
import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter";
|
|
||||||
import {UIEventSource} from "../Logic/UIEventSource";
|
import {UIEventSource} from "../Logic/UIEventSource";
|
||||||
import Minimap from "./Base/Minimap";
|
import Minimap, {MinimapObj} from "./Base/Minimap";
|
||||||
import Loc from "../Models/Loc";
|
import Loc from "../Models/Loc";
|
||||||
import BaseLayer from "../Models/BaseLayer";
|
import BaseLayer from "../Models/BaseLayer";
|
||||||
import {FixedUiElement} from "./Base/FixedUiElement";
|
import {FixedUiElement} from "./Base/FixedUiElement";
|
||||||
|
@ -14,7 +11,6 @@ import LayoutConfig from "../Models/ThemeConfig/LayoutConfig";
|
||||||
import FeaturePipeline from "../Logic/FeatureSource/FeaturePipeline";
|
import FeaturePipeline from "../Logic/FeatureSource/FeaturePipeline";
|
||||||
import ShowDataLayer from "./ShowDataLayer/ShowDataLayer";
|
import ShowDataLayer from "./ShowDataLayer/ShowDataLayer";
|
||||||
import {BBox} from "../Logic/BBox";
|
import {BBox} from "../Logic/BBox";
|
||||||
import ShowOverlayLayer from "./ShowDataLayer/ShowOverlayLayer";
|
|
||||||
/**
|
/**
|
||||||
* Creates screenshoter to take png screenshot
|
* Creates screenshoter to take png screenshot
|
||||||
* Creates jspdf and downloads it
|
* 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
|
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,
|
background: options.background,
|
||||||
allowMoving: false,
|
allowMoving: false,
|
||||||
|
onFullyLoaded: _ => window.setTimeout(() => {
|
||||||
|
|
||||||
onFullyLoaded: leaflet => window.setTimeout(() => {
|
|
||||||
if (self._screenhotTaken) {
|
if (self._screenhotTaken) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
self.CreatePdf(leaflet)
|
self.CreatePdf(minimap)
|
||||||
.then(() => self.cleanup())
|
.then(() => self.cleanup())
|
||||||
.catch(() => self.cleanup())
|
.catch(() => self.cleanup())
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -112,20 +106,17 @@ export default class ExportPDF {
|
||||||
this._screenhotTaken = true;
|
this._screenhotTaken = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async CreatePdf(leaflet: L.Map) {
|
private async CreatePdf(minimap: MinimapObj) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
console.log("PDF creation started")
|
console.log("PDF creation started")
|
||||||
const t = Translations.t.general.pdf;
|
const t = Translations.t.general.pdf;
|
||||||
const layout = this._layout
|
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');
|
let doc = new jsPDF('landscape');
|
||||||
|
|
||||||
|
const image = await minimap.TakeScreenshot()
|
||||||
const image = (await screenshotter.takeScreen('image'))
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
doc.addImage(image, 'PNG', 0, 0, this.mapW, this.mapH);
|
doc.addImage(image, 'PNG', 0, 0, this.mapW, this.mapH);
|
||||||
|
|
||||||
|
|
|
@ -167,6 +167,9 @@ export default class LocationInput extends InputElement<Loc> implements MinimapO
|
||||||
installBounds(factor: number | BBox, showRange?: boolean): void {
|
installBounds(factor: number | BBox, showRange?: boolean): void {
|
||||||
this.map.installBounds(factor, showRange)
|
this.map.installBounds(factor, showRange)
|
||||||
}
|
}
|
||||||
|
TakeScreenshot(): Promise<any> {
|
||||||
|
return this.map.TakeScreenshot()
|
||||||
|
}
|
||||||
|
|
||||||
protected InnerConstructElement(): HTMLElement {
|
protected InnerConstructElement(): HTMLElement {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -58,7 +58,6 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
||||||
for (const groupName of allGroupNames) {
|
for (const groupName of allGroupNames) {
|
||||||
const questions = layerConfig.tagRenderings.filter(tr => tr.group === groupName)
|
const questions = layerConfig.tagRenderings.filter(tr => tr.group === groupName)
|
||||||
const questionBox = new QuestionBox(tags, questions, layerConfig.units);
|
const questionBox = new QuestionBox(tags, questions, layerConfig.units);
|
||||||
console.log("Groupname:", groupName)
|
|
||||||
questionBoxes.set(groupName, questionBox)
|
questionBoxes.set(groupName, questionBox)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,7 +155,6 @@ export default class ShowDataLayer {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if ((feat.geometry.type === "LineString" || feat.geometry.type === "MultiLineString")) {
|
if ((feat.geometry.type === "LineString" || feat.geometry.type === "MultiLineString")) {
|
||||||
const self = this;
|
const self = this;
|
||||||
const coords = L.GeoJSON.coordsToLatLngs(feat.geometry.coordinates)
|
const coords = L.GeoJSON.coordsToLatLngs(feat.geometry.coordinates)
|
||||||
|
@ -190,9 +189,10 @@ export default class ShowDataLayer {
|
||||||
|
|
||||||
if (options.zoomToFeatures ?? false) {
|
if (options.zoomToFeatures ?? false) {
|
||||||
try {
|
try {
|
||||||
mp.fitBounds(this.geoLayer.getBounds(), {animate: false})
|
const bounds = this.geoLayer.getBounds()
|
||||||
|
mp.fitBounds(bounds, {animate: false})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.debug("Invalid bounds",e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ import Histogram from "./BigComponents/Histogram";
|
||||||
import Loc from "../Models/Loc";
|
import Loc from "../Models/Loc";
|
||||||
import {Utils} from "../Utils";
|
import {Utils} from "../Utils";
|
||||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
|
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
|
||||||
import ImportButton, {ImportButtonSpecialViz} from "./BigComponents/ImportButton";
|
import {ImportButtonSpecialViz} from "./BigComponents/ImportButton";
|
||||||
import {Tag} from "../Logic/Tags/Tag";
|
import {Tag} from "../Logic/Tags/Tag";
|
||||||
import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource";
|
import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource";
|
||||||
import ShowDataMultiLayer from "./ShowDataLayer/ShowDataMultiLayer";
|
import ShowDataMultiLayer from "./ShowDataLayer/ShowDataMultiLayer";
|
||||||
|
@ -38,9 +38,9 @@ import {SubtleButton} from "./Base/SubtleButton";
|
||||||
import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction";
|
import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction";
|
||||||
import {And} from "../Logic/Tags/And";
|
import {And} from "../Logic/Tags/And";
|
||||||
import Toggle from "./Input/Toggle";
|
import Toggle from "./Input/Toggle";
|
||||||
import {DefaultGuiState} from "./DefaultGUI";
|
|
||||||
import Img from "./Base/Img";
|
import Img from "./Base/Img";
|
||||||
import FilteredLayer from "../Models/FilteredLayer";
|
import FilteredLayer from "../Models/FilteredLayer";
|
||||||
|
import {DefaultGuiState} from "./DefaultGuiState";
|
||||||
|
|
||||||
export interface SpecialVisualization {
|
export interface SpecialVisualization {
|
||||||
funcName: string,
|
funcName: string,
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {Utils} from "../Utils";
|
||||||
import {VariableUiElement} from "./Base/VariableUIElement";
|
import {VariableUiElement} from "./Base/VariableUIElement";
|
||||||
import Combine from "./Base/Combine";
|
import Combine from "./Base/Combine";
|
||||||
import BaseUIElement from "./BaseUIElement";
|
import BaseUIElement from "./BaseUIElement";
|
||||||
import {DefaultGuiState} from "./DefaultGUI";
|
import {DefaultGuiState} from "./DefaultGuiState";
|
||||||
|
|
||||||
export class SubstitutedTranslation extends VariableUiElement {
|
export class SubstitutedTranslation extends VariableUiElement {
|
||||||
|
|
||||||
|
|
32
assets/layers/conflation/conflation.json
Normal file
32
assets/layers/conflation/conflation.json
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
15
assets/layers/gps_location/gps_location.json
Normal file
15
assets/layers/gps_location/gps_location.json
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -5,15 +5,6 @@
|
||||||
"source": {
|
"source": {
|
||||||
"osmTags": "user:home=yes"
|
"osmTags": "user:home=yes"
|
||||||
},
|
},
|
||||||
"icon": {
|
|
||||||
"render": "circle:white;./assets/svg/home.svg"
|
|
||||||
},
|
|
||||||
"iconSize": {
|
|
||||||
"render": "20,20,center"
|
|
||||||
},
|
|
||||||
"color": {
|
|
||||||
"render": "#00f"
|
|
||||||
},
|
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
{
|
{
|
||||||
"icon": {
|
"icon": {
|
||||||
|
@ -22,14 +13,7 @@
|
||||||
"iconSize": {
|
"iconSize": {
|
||||||
"render": "20,20,center"
|
"render": "20,20,center"
|
||||||
},
|
},
|
||||||
"location": [
|
"location": "point"
|
||||||
"point"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": {
|
|
||||||
"render": "#00f"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -235,11 +235,6 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
{
|
|
||||||
"location": [
|
|
||||||
"point"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"color": {
|
"color": {
|
||||||
"render": "#ff7392",
|
"render": "#ff7392",
|
||||||
|
|
|
@ -38,6 +38,8 @@
|
||||||
"override": {
|
"override": {
|
||||||
"calculatedTags": [
|
"calculatedTags": [
|
||||||
"_is_part_of_building=feat.get('parent_ways')?.some(p => p.building !== undefined && p.building !== '') ?? false",
|
"_is_part_of_building=feat.get('parent_ways')?.some(p => p.building !== undefined && p.building !== '') ?? 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"
|
"_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": [
|
"mappings": [
|
||||||
{
|
{
|
||||||
"if": "_overlaps_with!=null",
|
"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)}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
3
index.ts
3
index.ts
|
@ -9,10 +9,11 @@ import {Utils} from "./Utils";
|
||||||
import AllThemesGui from "./UI/AllThemesGui";
|
import AllThemesGui from "./UI/AllThemesGui";
|
||||||
import DetermineLayout from "./Logic/DetermineLayout";
|
import DetermineLayout from "./Logic/DetermineLayout";
|
||||||
import LayoutConfig from "./Models/ThemeConfig/LayoutConfig";
|
import LayoutConfig from "./Models/ThemeConfig/LayoutConfig";
|
||||||
import DefaultGUI, {DefaultGuiState} from "./UI/DefaultGUI";
|
import DefaultGUI from "./UI/DefaultGUI";
|
||||||
import State from "./State";
|
import State from "./State";
|
||||||
import AvailableBaseLayersImplementation from "./Logic/Actors/AvailableBaseLayersImplementation";
|
import AvailableBaseLayersImplementation from "./Logic/Actors/AvailableBaseLayersImplementation";
|
||||||
import ShowOverlayLayerImplementation from "./UI/ShowDataLayer/ShowOverlayLayerImplementation";
|
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
|
// 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()
|
MinimapImplementation.initialize()
|
||||||
|
|
163
test.ts
163
test.ts
|
@ -1,139 +1,26 @@
|
||||||
import {Utils} from "./Utils";
|
import ShowDataLayer from "./UI/ShowDataLayer/ShowDataLayer";
|
||||||
import FullNodeDatabaseSource from "./Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource";
|
import AllKnownLayers from "./Customizations/AllKnownLayers";
|
||||||
|
import Minimap from "./UI/Base/Minimap";
|
||||||
|
import StaticFeatureSource from "./Logic/FeatureSource/Sources/StaticFeatureSource";
|
||||||
const data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
import MinimapImplementation from "./UI/Base/MinimapImplementation";
|
||||||
"<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" +
|
import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers";
|
||||||
" <bounds minlat=\"51.2154864\" minlon=\"3.2176208\" maxlat=\"51.2163466\" maxlon=\"3.2189941\"/>\n" +
|
import BaseLayer from "./Models/BaseLayer";
|
||||||
" <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" +
|
import {UIEventSource} from "./Logic/UIEventSource";
|
||||||
" <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" +
|
import AvailableBaseLayersImplementation from "./Logic/Actors/AvailableBaseLayersImplementation";
|
||||||
" <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" +
|
MinimapImplementation.initialize()
|
||||||
" <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" +
|
AvailableBaseLayers.implement(new AvailableBaseLayersImplementation())
|
||||||
" <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" +
|
const confirmationMap = Minimap.createMiniMap({
|
||||||
" <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" +
|
background: new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto)
|
||||||
" <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)
|
|
||||||
})
|
})
|
||||||
|
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")
|
185
test/ReplaceGeometry.spec.ts
Normal file
185
test/ReplaceGeometry.spec.ts
Normal 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import TileFreshnessCalculatorSpec from "./TileFreshnessCalculator.spec";
|
||||||
import WikidataSpecTest from "./Wikidata.spec.test";
|
import WikidataSpecTest from "./Wikidata.spec.test";
|
||||||
import ImageProviderSpec from "./ImageProvider.spec";
|
import ImageProviderSpec from "./ImageProvider.spec";
|
||||||
import ActorsSpec from "./Actors.spec";
|
import ActorsSpec from "./Actors.spec";
|
||||||
|
import ReplaceGeometrySpec from "./ReplaceGeometry.spec";
|
||||||
|
|
||||||
|
|
||||||
ScriptUtils.fixUtils()
|
ScriptUtils.fixUtils()
|
||||||
|
@ -29,7 +30,8 @@ const allTests = [
|
||||||
new TileFreshnessCalculatorSpec(),
|
new TileFreshnessCalculatorSpec(),
|
||||||
new WikidataSpecTest(),
|
new WikidataSpecTest(),
|
||||||
new ImageProviderSpec(),
|
new ImageProviderSpec(),
|
||||||
new ActorsSpec()
|
new ActorsSpec(),
|
||||||
|
new ReplaceGeometrySpec()
|
||||||
]
|
]
|
||||||
|
|
||||||
Utils.externalDownloadFunction = async (url) => {
|
Utils.externalDownloadFunction = async (url) => {
|
||||||
|
|
Loading…
Reference in a new issue