Cleaning filtered layer
This commit is contained in:
parent
8e5e249e6b
commit
314894085a
12 changed files with 232 additions and 280 deletions
|
@ -116,4 +116,63 @@ export default class LayerConfig {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public GenerateLeafletStyle(tags: any):
|
||||||
|
{
|
||||||
|
color: string;
|
||||||
|
icon: { popupAnchor: [number, number]; iconAnchor: [number, number]; iconSize: [number, number]; iconUrl: string }; weight: number; dashArray: number[]
|
||||||
|
} {
|
||||||
|
const iconUrl = this.icon?.GetRenderValue(tags)?.txt;
|
||||||
|
const iconSize = (this.iconSize?.GetRenderValue(tags)?.txt ?? "40,40,center").split(",");
|
||||||
|
|
||||||
|
|
||||||
|
const dashArray = this.dashArray.GetRenderValue(tags)?.txt.split(" ").map(Number);
|
||||||
|
|
||||||
|
function num(str, deflt = 40) {
|
||||||
|
const n = Number(str);
|
||||||
|
if (isNaN(n)) {
|
||||||
|
return deflt;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
const iconW = num(iconSize[0]);
|
||||||
|
const iconH = num(iconSize[1]);
|
||||||
|
const mode = iconSize[2] ?? "center"
|
||||||
|
|
||||||
|
let anchorW = iconW / 2;
|
||||||
|
let anchorH = iconH / 2;
|
||||||
|
if (mode === "left") {
|
||||||
|
anchorW = 0;
|
||||||
|
}
|
||||||
|
if (mode === "right") {
|
||||||
|
anchorW = iconW;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === "top") {
|
||||||
|
anchorH = 0;
|
||||||
|
}
|
||||||
|
if (mode === "bottom") {
|
||||||
|
anchorH = iconH;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const color = this.color?.GetRenderValue(tags)?.txt ?? "#00f";
|
||||||
|
let weight = num(this.width?.GetRenderValue(tags)?.txt, 5);
|
||||||
|
return {
|
||||||
|
icon:
|
||||||
|
{
|
||||||
|
iconUrl: iconUrl,
|
||||||
|
iconSize: [iconW, iconH],
|
||||||
|
iconAnchor: [anchorW, anchorH],
|
||||||
|
popupAnchor: [0, 3 - anchorH]
|
||||||
|
},
|
||||||
|
color: color,
|
||||||
|
weight: weight,
|
||||||
|
dashArray: dashArray
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -475,18 +475,16 @@ export class InitUiElements {
|
||||||
throw "Layer " + layer + " was not substituted";
|
throw "Layer " + layer + " was not substituted";
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateInfo = (tagsES) => {
|
const flayer: FilteredLayer = new FilteredLayer(layer,
|
||||||
|
(tagsES) => {
|
||||||
return new FeatureInfoBox(
|
return new FeatureInfoBox(
|
||||||
tagsES,
|
tagsES,
|
||||||
layer,
|
layer,
|
||||||
)
|
)
|
||||||
};
|
});
|
||||||
|
|
||||||
const flayer: FilteredLayer = FilteredLayer.fromDefinition(layer, generateInfo);
|
|
||||||
flayers.push(flayer);
|
flayers.push(flayer);
|
||||||
|
|
||||||
QueryParameters.GetQueryParameter("layer-" + layer.id, "true", "Wehter or not layer "+layer.id+" is shown")
|
QueryParameters.GetQueryParameter("layer-" + layer.id, "true", "Wehter or not layer " + layer.id + " is shown")
|
||||||
.map<boolean>((str) => str !== "false", [], (b) => b.toString())
|
.map<boolean>((str) => str !== "false", [], (b) => b.toString())
|
||||||
.syncWith(
|
.syncWith(
|
||||||
flayer.isDisplayed
|
flayer.isDisplayed
|
||||||
|
|
|
@ -25,13 +25,9 @@ export class FilteredLayer {
|
||||||
public readonly layerDef: LayerConfig;
|
public readonly layerDef: LayerConfig;
|
||||||
private readonly _maxAllowedOverlap: number;
|
private readonly _maxAllowedOverlap: number;
|
||||||
|
|
||||||
private readonly _style: (properties) => { color: string, weight?: number, icon: { iconUrl: string, iconSize?: [number, number], popupAnchor?: [number, number], iconAnchor?: [number, number] } };
|
|
||||||
|
|
||||||
|
|
||||||
/** The featurecollection from overpass
|
/** The featurecollection from overpass
|
||||||
*/
|
*/
|
||||||
private _dataFromOverpass: any[];
|
private _dataFromOverpass: any[];
|
||||||
private readonly _wayHandling: number;
|
|
||||||
/** List of new elements, geojson features
|
/** List of new elements, geojson features
|
||||||
*/
|
*/
|
||||||
private _newElements = [];
|
private _newElements = [];
|
||||||
|
@ -49,60 +45,7 @@ export class FilteredLayer {
|
||||||
) {
|
) {
|
||||||
this.layerDef = layerDef;
|
this.layerDef = layerDef;
|
||||||
|
|
||||||
this._wayHandling = layerDef.wayHandling;
|
|
||||||
this._showOnPopup = showOnPopup;
|
this._showOnPopup = showOnPopup;
|
||||||
this._style = (tags) => {
|
|
||||||
|
|
||||||
const iconUrl = layerDef.icon?.GetRenderValue(tags)?.txt;
|
|
||||||
const iconSize = (layerDef.iconSize?.GetRenderValue(tags)?.txt ?? "40,40,center").split(",");
|
|
||||||
|
|
||||||
|
|
||||||
const dashArray = layerDef.dashArray.GetRenderValue(tags)?.txt.split(" ").map(Number);
|
|
||||||
|
|
||||||
function num(str, deflt = 40) {
|
|
||||||
const n = Number(str);
|
|
||||||
if (isNaN(n)) {
|
|
||||||
return deflt;
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
const iconW = num(iconSize[0]);
|
|
||||||
const iconH = num(iconSize[1]);
|
|
||||||
const mode = iconSize[2] ?? "center"
|
|
||||||
|
|
||||||
let anchorW = iconW / 2;
|
|
||||||
let anchorH = iconH / 2;
|
|
||||||
if (mode === "left") {
|
|
||||||
anchorW = 0;
|
|
||||||
}
|
|
||||||
if (mode === "right") {
|
|
||||||
anchorW = iconW;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode === "top") {
|
|
||||||
anchorH = 0;
|
|
||||||
}
|
|
||||||
if (mode === "bottom") {
|
|
||||||
anchorH = iconH;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const color = layerDef.color?.GetRenderValue(tags)?.txt ?? "#00f";
|
|
||||||
let weight = num(layerDef.width?.GetRenderValue(tags)?.txt, 5);
|
|
||||||
return {
|
|
||||||
icon:
|
|
||||||
{
|
|
||||||
iconUrl: iconUrl,
|
|
||||||
iconSize: [iconW, iconH],
|
|
||||||
iconAnchor: [anchorW, anchorH],
|
|
||||||
popupAnchor: [0, 3 - anchorH]
|
|
||||||
},
|
|
||||||
color: color,
|
|
||||||
weight: weight,
|
|
||||||
dashArray: dashArray
|
|
||||||
};
|
|
||||||
};
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.filters = layerDef.overpassTags;
|
this.filters = layerDef.overpassTags;
|
||||||
this._maxAllowedOverlap = layerDef.hideUnderlayingFeaturesMinPercentage;
|
this._maxAllowedOverlap = layerDef.hideUnderlayingFeaturesMinPercentage;
|
||||||
|
@ -123,13 +66,6 @@ export class FilteredLayer {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromDefinition(definition, showOnPopup: (tags: UIEventSource<any>, feature: any) => UIElement): FilteredLayer {
|
|
||||||
return new FilteredLayer(definition, showOnPopup);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main function to load data into this layer.
|
* The main function to load data into this layer.
|
||||||
* The data that is NOT used by this layer, is returned as a geojson object; the other data is rendered
|
* The data that is NOT used by this layer, is returned as a geojson object; the other data is rendered
|
||||||
|
@ -138,32 +74,15 @@ export class FilteredLayer {
|
||||||
const leftoverFeatures = [];
|
const leftoverFeatures = [];
|
||||||
const selfFeatures = [];
|
const selfFeatures = [];
|
||||||
for (let feature of geojson.features) {
|
for (let feature of geojson.features) {
|
||||||
// feature.properties contains all the properties
|
|
||||||
|
|
||||||
const tags = TagUtils.proprtiesToKV(feature.properties);
|
const tags = TagUtils.proprtiesToKV(feature.properties);
|
||||||
|
|
||||||
if (!this.filters.matches(tags)) {
|
if (!this.filters.matches(tags)) {
|
||||||
leftoverFeatures.push(feature);
|
leftoverFeatures.push(feature);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (feature.geometry.type !== "Point") {
|
|
||||||
const centerPoint = GeoOperations.centerpoint(feature);
|
|
||||||
if (this._wayHandling === LayerConfig.WAYHANDLING_CENTER_AND_WAY) {
|
|
||||||
selfFeatures.push(centerPoint);
|
|
||||||
} else if (this._wayHandling === LayerConfig.WAYHANDLING_CENTER_ONLY) {
|
|
||||||
feature = centerPoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
selfFeatures.push(feature);
|
selfFeatures.push(feature);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.RenderLayer(selfFeatures)
|
||||||
this.RenderLayer({
|
|
||||||
type: "FeatureCollection",
|
|
||||||
features: selfFeatures
|
|
||||||
})
|
|
||||||
|
|
||||||
const notShadowed = [];
|
const notShadowed = [];
|
||||||
for (const feature of leftoverFeatures) {
|
for (const feature of leftoverFeatures) {
|
||||||
|
@ -186,18 +105,140 @@ export class FilteredLayer {
|
||||||
|
|
||||||
public AddNewElement(element) {
|
public AddNewElement(element) {
|
||||||
this._newElements.push(element);
|
this._newElements.push(element);
|
||||||
this.RenderLayer({features: this._dataFromOverpass}, element); // Update the layer
|
this.RenderLayer( this._dataFromOverpass); // Update the layer
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private RenderLayer(data, openPopupOf = undefined) {
|
private RenderLayer(features) {
|
||||||
let self = this;
|
|
||||||
|
|
||||||
if (this._geolayer !== undefined && this._geolayer !== null) {
|
if (this._geolayer !== undefined && this._geolayer !== null) {
|
||||||
// Remove the old geojson layer from the map - we'll reshow all the elements later on anyway
|
// Remove the old geojson layer from the map - we'll reshow all the elements later on anyway
|
||||||
State.state.bm.map.removeLayer(this._geolayer);
|
State.state.bm.map.removeLayer(this._geolayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We fetch all the data we have to show:
|
||||||
|
let fusedFeatures = this.ApplyWayHandling(this.FuseData(features));
|
||||||
|
console.log("Fused:",fusedFeatures)
|
||||||
|
|
||||||
|
// And we copy some features as points - if needed
|
||||||
|
const data = {
|
||||||
|
type: "FeatureCollection",
|
||||||
|
features: fusedFeatures
|
||||||
|
}
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
console.log(data);
|
||||||
|
this._geolayer = L.geoJSON(data, {
|
||||||
|
/* style: feature => {
|
||||||
|
self.layerDef.GenerateLeafletStyle(feature.properties);
|
||||||
|
return {
|
||||||
|
color: "#f00",
|
||||||
|
weight: 4
|
||||||
|
}
|
||||||
|
},*/
|
||||||
|
/*
|
||||||
|
pointToLayer: function (feature, latLng) {
|
||||||
|
// Point to layer converts the 'point' to a layer object - as the geojson layer natively cannot handle points
|
||||||
|
// Click handling is done in the next step
|
||||||
|
|
||||||
|
const style = self.layerDef.GenerateLeafletStyle(feature.properties);
|
||||||
|
let marker;
|
||||||
|
if (style.icon === undefined) {
|
||||||
|
marker = L.circle(latLng, {
|
||||||
|
radius: 25,
|
||||||
|
color: style.color
|
||||||
|
});
|
||||||
|
} else if (style.icon.iconUrl.startsWith("$circle")) {
|
||||||
|
marker = L.circle(latLng, {
|
||||||
|
radius: 25,
|
||||||
|
color: style.color
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (style.icon.iconSize === undefined) {
|
||||||
|
style.icon.iconSize = [50, 50]
|
||||||
|
}
|
||||||
|
|
||||||
|
marker = L.marker(latLng, {
|
||||||
|
icon: L.icon(style.icon)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return marker;
|
||||||
|
},*/
|
||||||
|
/*
|
||||||
|
onEachFeature: function (feature, layer:Layer) {
|
||||||
|
|
||||||
|
layer.on("click", (e) => {
|
||||||
|
if (layer.getPopup() === undefined
|
||||||
|
&& (window.screen.availHeight > 600 || window.screen.availWidth > 600) // We DON'T trigger this code on small screens! No need to create a popup
|
||||||
|
) {
|
||||||
|
const popup = L.popup({
|
||||||
|
autoPan: true,
|
||||||
|
closeOnEscapeKey: true,
|
||||||
|
}, layer);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
popup.setLatLng(e.latlng)
|
||||||
|
|
||||||
|
layer.bindPopup(popup);
|
||||||
|
const eventSource = State.state.allElements.addOrGetElement(feature);
|
||||||
|
const uiElement = self._showOnPopup(eventSource, feature);
|
||||||
|
// We first render the UIelement (which'll still need an update later on...)
|
||||||
|
// But at least it'll be visible already
|
||||||
|
popup.setContent(uiElement.Render());
|
||||||
|
popup.openOn(State.state.bm.map);
|
||||||
|
// popup.openOn(State.state.bm.map);
|
||||||
|
// ANd we perform the pending update
|
||||||
|
uiElement.Update();
|
||||||
|
}
|
||||||
|
// We set the element as selected...
|
||||||
|
State.state.selectedElement.setData(feature);
|
||||||
|
|
||||||
|
// We mark the event as consumed
|
||||||
|
L.DomEvent.stop(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
)
|
||||||
|
;
|
||||||
|
|
||||||
|
if (this.combinedIsDisplayed.data) {
|
||||||
|
this._geolayer.addTo(State.state.bm.map);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private ApplyWayHandling(fusedFeatures: any[]) {
|
||||||
|
if (this.layerDef.wayHandling === LayerConfig.WAYHANDLING_DEFAULT) {
|
||||||
|
// We don't have to do anything special
|
||||||
|
return fusedFeatures;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// We have to convert all the ways into centerpoints
|
||||||
|
const existingPoints = [];
|
||||||
|
const newPoints = [];
|
||||||
|
const existingWays = [];
|
||||||
|
|
||||||
|
for (const feature of fusedFeatures) {
|
||||||
|
if (feature.geometry.type === "Point") {
|
||||||
|
existingPoints.push(feature);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
existingWays.push(feature);
|
||||||
|
const centerPoint = GeoOperations.centerpoint(feature);
|
||||||
|
newPoints.push(centerPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
fusedFeatures = existingPoints.concat(newPoints);
|
||||||
|
if (this.layerDef.wayHandling === LayerConfig.WAYHANDLING_CENTER_AND_WAY) {
|
||||||
|
fusedFeatures = fusedFeatures.concat(existingWays)
|
||||||
|
}
|
||||||
|
return fusedFeatures;
|
||||||
|
}
|
||||||
|
|
||||||
|
//*Fuses the old and the new datasets*/
|
||||||
|
private FuseData(data: any[]) {
|
||||||
const oldData = this._dataFromOverpass ?? [];
|
const oldData = this._dataFromOverpass ?? [];
|
||||||
|
|
||||||
// We keep track of all the ids that are freshly loaded in order to avoid adding duplicates
|
// We keep track of all the ids that are freshly loaded in order to avoid adding duplicates
|
||||||
|
@ -205,7 +246,7 @@ export class FilteredLayer {
|
||||||
// A list of all the features to show
|
// A list of all the features to show
|
||||||
const fusedFeatures = [];
|
const fusedFeatures = [];
|
||||||
// First, we add all the fresh data:
|
// First, we add all the fresh data:
|
||||||
for (const feature of data.features) {
|
for (const feature of data) {
|
||||||
idsFromOverpass.add(feature.properties.id);
|
idsFromOverpass.add(feature.properties.id);
|
||||||
fusedFeatures.push(feature);
|
fusedFeatures.push(feature);
|
||||||
}
|
}
|
||||||
|
@ -226,133 +267,6 @@ export class FilteredLayer {
|
||||||
fusedFeatures.push(feature);
|
fusedFeatures.push(feature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return fusedFeatures;
|
||||||
|
|
||||||
// We use a new, fused dataset
|
|
||||||
data = {
|
|
||||||
type: "FeatureCollection",
|
|
||||||
features: fusedFeatures
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// The data is split in two parts: the point and the rest
|
|
||||||
// The points get a special treatment in order to render them properly
|
|
||||||
// Note that some features might get a point representation as well
|
|
||||||
|
|
||||||
const runWhenAdded: (() => void)[] = []
|
|
||||||
|
|
||||||
this._geolayer = L.geoJSON(data, {
|
|
||||||
style: function (feature) {
|
|
||||||
return self._style(feature.properties);
|
|
||||||
},
|
|
||||||
pointToLayer: function (feature, latLng) {
|
|
||||||
const style = self._style(feature.properties);
|
|
||||||
let marker;
|
|
||||||
if (style.icon === undefined) {
|
|
||||||
marker = L.circle(latLng, {
|
|
||||||
radius: 25,
|
|
||||||
color: style.color
|
|
||||||
});
|
|
||||||
|
|
||||||
} else if (style.icon.iconUrl.startsWith("$circle")) {
|
|
||||||
marker = L.circle(latLng, {
|
|
||||||
radius: 25,
|
|
||||||
color: style.color
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (style.icon.iconSize === undefined) {
|
|
||||||
style.icon.iconSize = [50, 50]
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
marker = L.marker(latLng, {
|
|
||||||
icon: L.icon(style.icon),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let eventSource = State.state.allElements.addOrGetElement(feature);
|
|
||||||
const popup = L.popup({}, marker);
|
|
||||||
let uiElement: UIElement;
|
|
||||||
let content = undefined;
|
|
||||||
let p = marker.bindPopup(popup)
|
|
||||||
.on("popupopen", () => {
|
|
||||||
if (content === undefined) {
|
|
||||||
uiElement = self._showOnPopup(eventSource, feature);
|
|
||||||
// Lazily create the content
|
|
||||||
content = uiElement.Render();
|
|
||||||
}
|
|
||||||
popup.setContent(content);
|
|
||||||
uiElement.Update();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (feature === openPopupOf) {
|
|
||||||
runWhenAdded.push(() => {
|
|
||||||
p.openPopup();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return marker;
|
|
||||||
},
|
|
||||||
|
|
||||||
onEachFeature: function (feature, layer:Layer) {
|
|
||||||
|
|
||||||
// We monky-patch the feature element with an update-style
|
|
||||||
function updateStyle () {
|
|
||||||
// @ts-ignore
|
|
||||||
if (layer.setIcon) {
|
|
||||||
const style = self._style(feature.properties);
|
|
||||||
const icon = style.icon;
|
|
||||||
if (icon.iconUrl) {
|
|
||||||
if (icon.iconUrl.startsWith("$circle")) {
|
|
||||||
// pass
|
|
||||||
} else {
|
|
||||||
// @ts-ignore
|
|
||||||
layer.setIcon(L.icon(icon))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self._geolayer.setStyle(function (featureX) {
|
|
||||||
return self._style(featureX.properties);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let eventSource = State.state.allElements.addOrGetElement(feature);
|
|
||||||
|
|
||||||
|
|
||||||
eventSource.addCallback(updateStyle);
|
|
||||||
|
|
||||||
function openPopup(e) {
|
|
||||||
if (feature.geometry.type === "Point") {
|
|
||||||
return; // Points bind their own popups
|
|
||||||
}
|
|
||||||
const uiElement = self._showOnPopup(eventSource, feature);
|
|
||||||
L.popup({
|
|
||||||
autoPan: true,
|
|
||||||
}).setContent(uiElement.Render())
|
|
||||||
.setLatLng(e.latlng)
|
|
||||||
.openOn(State.state.bm.map);
|
|
||||||
uiElement.Update();
|
|
||||||
if (e) {
|
|
||||||
L.DomEvent.stop(e); // Marks the event as consumed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
layer.on("click", (e) => {
|
|
||||||
updateStyle();
|
|
||||||
openPopup(e);
|
|
||||||
State.state.selectedElement.setData(feature);
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.combinedIsDisplayed.data) {
|
|
||||||
this._geolayer.addTo(State.state.bm.map);
|
|
||||||
for (const f of runWhenAdded) {
|
|
||||||
f();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -8,34 +8,13 @@ import Combine from "./Base/Combine";
|
||||||
*/
|
*/
|
||||||
export class FullScreenMessageBox extends UIElement {
|
export class FullScreenMessageBox extends UIElement {
|
||||||
|
|
||||||
private static readonly _toTheMap_height : string = "5em";
|
|
||||||
|
|
||||||
private readonly returnToTheMap: UIElement;
|
private readonly returnToTheMap: UIElement;
|
||||||
private _content: UIElement;
|
private _content: UIElement;
|
||||||
|
|
||||||
constructor(onClear: (() => void)) {
|
constructor(onClear: (() => void)) {
|
||||||
super();
|
super(State.state.fullScreenMessage);
|
||||||
this.HideOnEmpty(true);
|
this.HideOnEmpty(true);
|
||||||
const self = this;
|
const self = this;
|
||||||
State.state.fullScreenMessage.addCallbackAndRun(uiElement => {
|
|
||||||
self.Update();
|
|
||||||
if (uiElement === undefined) {
|
|
||||||
location.hash = "";
|
|
||||||
} else {
|
|
||||||
// The 'hash' makes sure a new piece of history is added. This makes the 'back-button' on android remove the popup
|
|
||||||
location.hash = "#element";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (window !== undefined) {
|
|
||||||
window.onhashchange = function () {
|
|
||||||
if (location.hash === "") {
|
|
||||||
// No more element: back to the map!
|
|
||||||
State.state.fullScreenMessage.setData(undefined);
|
|
||||||
onClear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.returnToTheMap =
|
this.returnToTheMap =
|
||||||
new Combine([Translations.t.general.returnToTheMap.Clone().SetStyle("font-size:xx-large")])
|
new Combine([Translations.t.general.returnToTheMap.Clone().SetStyle("font-size:xx-large")])
|
||||||
|
@ -44,7 +23,7 @@ export class FullScreenMessageBox extends UIElement {
|
||||||
"z-index: 10000;" +
|
"z-index: 10000;" +
|
||||||
"bottom: 0;" +
|
"bottom: 0;" +
|
||||||
"left: 0;" +
|
"left: 0;" +
|
||||||
`height: ${FullScreenMessageBox._toTheMap_height};` +
|
`height: var(--return-to-the-map-height);` +
|
||||||
"width: 100vw;" +
|
"width: 100vw;" +
|
||||||
"color: var(--catch-detail-color-contrast);" +
|
"color: var(--catch-detail-color-contrast);" +
|
||||||
"font-weight: bold;" +
|
"font-weight: bold;" +
|
||||||
|
@ -55,10 +34,8 @@ export class FullScreenMessageBox extends UIElement {
|
||||||
"padding-bottom: 1.2em;" +
|
"padding-bottom: 1.2em;" +
|
||||||
"box-sizing:border-box")
|
"box-sizing:border-box")
|
||||||
.onClick(() => {
|
.onClick(() => {
|
||||||
console.log("Returning...")
|
|
||||||
State.state.fullScreenMessage.setData(undefined);
|
State.state.fullScreenMessage.setData(undefined);
|
||||||
onClear();
|
onClear();
|
||||||
self.Update();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -73,9 +50,9 @@ export class FullScreenMessageBox extends UIElement {
|
||||||
"display:block;" +
|
"display:block;" +
|
||||||
"padding: 1em;" +
|
"padding: 1em;" +
|
||||||
"padding-bottom:6em;" +
|
"padding-bottom:6em;" +
|
||||||
`margin-bottom:${FullScreenMessageBox._toTheMap_height};` +
|
`margin-bottom: var(--return-to-the-map-height);` +
|
||||||
"box-sizing:border-box;" +
|
"box-sizing:border-box;" +
|
||||||
`height:calc(100vh - ${FullScreenMessageBox._toTheMap_height});` +
|
`height:calc(100vh - var(--return-to-the-map-height));` +
|
||||||
"overflow-y: auto;" +
|
"overflow-y: auto;" +
|
||||||
"max-width:100vw;" +
|
"max-width:100vw;" +
|
||||||
"overflow-x:hidden;" +
|
"overflow-x:hidden;" +
|
||||||
|
|
|
@ -68,7 +68,6 @@ export default class DirectionInput extends InputElement<string> {
|
||||||
|
|
||||||
htmlElement.ontouchstart = (ev: TouchEvent) => {
|
htmlElement.ontouchstart = (ev: TouchEvent) => {
|
||||||
onPosChange(ev.touches[0].clientX, ev.touches[0].clientY);
|
onPosChange(ev.touches[0].clientX, ev.touches[0].clientY);
|
||||||
ev.preventDefault();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let isDown = false;
|
let isDown = false;
|
||||||
|
|
|
@ -27,24 +27,25 @@ export default class EditableTagRendering extends UIElement {
|
||||||
this.ListenTo(this._editMode);
|
this.ListenTo(this._editMode);
|
||||||
this.ListenTo(State.state?.osmConnection?.userDetails)
|
this.ListenTo(State.state?.osmConnection?.userDetails)
|
||||||
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
this._answer = new TagRenderingAnswer(tags, configuration);
|
this._answer = new TagRenderingAnswer(tags, configuration);
|
||||||
|
|
||||||
this._answer.SetStyle("width:100%;")
|
this._answer.SetStyle("width:100%;")
|
||||||
|
|
||||||
if (this._configuration.question !== undefined) {
|
if (this._configuration.question !== undefined) {
|
||||||
// 2.3em total width
|
if (State.state.featureSwitchUserbadge.data) {
|
||||||
if(State.state.featureSwitchUserbadge.data){
|
// 2.3em total width
|
||||||
|
const self = this;
|
||||||
this._editButton =
|
this._editButton =
|
||||||
Svg.pencil_svg().SetClass("edit-button")
|
Svg.pencil_svg().SetClass("edit-button")
|
||||||
.onClick(() => {
|
.onClick(() => {
|
||||||
self._editMode.setData(true);
|
self._editMode.setData(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private GenerateQuestion() {
|
||||||
|
const self = this;
|
||||||
|
if (this._configuration.question !== undefined) {
|
||||||
// And at last, set up the skip button
|
// And at last, set up the skip button
|
||||||
const cancelbutton =
|
const cancelbutton =
|
||||||
Translations.t.general.cancel.Clone()
|
Translations.t.general.cancel.Clone()
|
||||||
|
@ -53,7 +54,7 @@ export default class EditableTagRendering extends UIElement {
|
||||||
self._editMode.setData(false)
|
self._editMode.setData(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
this._question = new TagRenderingQuestion(tags, configuration,
|
return new TagRenderingQuestion(this._tags, this._configuration,
|
||||||
() => {
|
() => {
|
||||||
self._editMode.setData(false)
|
self._editMode.setData(false)
|
||||||
},
|
},
|
||||||
|
@ -65,6 +66,7 @@ export default class EditableTagRendering extends UIElement {
|
||||||
InnerRender(): string {
|
InnerRender(): string {
|
||||||
|
|
||||||
if (this._editMode.data) {
|
if (this._editMode.data) {
|
||||||
|
this._question = this.GenerateQuestion();
|
||||||
return this._question.Render();
|
return this._question.Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -118,7 +118,6 @@ export default class TagRenderingQuestion extends UIElement {
|
||||||
const inputEl = new InputElementMap<number[], TagsFilter>(
|
const inputEl = new InputElementMap<number[], TagsFilter>(
|
||||||
checkBoxes,
|
checkBoxes,
|
||||||
(t0, t1) => {
|
(t0, t1) => {
|
||||||
console.log("IsEquiv?",t0, t1, t0?.isEquivalent(t1))
|
|
||||||
return t0?.isEquivalent(t1) ?? false
|
return t0?.isEquivalent(t1) ?? false
|
||||||
},
|
},
|
||||||
(indices) => {
|
(indices) => {
|
||||||
|
@ -162,8 +161,6 @@ export default class TagRenderingQuestion extends UIElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
console.log(indices, freeformExtras);
|
|
||||||
|
|
||||||
if (freeformField) {
|
if (freeformField) {
|
||||||
if (freeformExtras.length > 0) {
|
if (freeformExtras.length > 0) {
|
||||||
freeformField.GetValue().setData(new Tag(this._configuration.freeform.key, freeformExtras.join(";")));
|
freeformField.GetValue().setData(new Tag(this._configuration.freeform.key, freeformExtras.join(";")));
|
||||||
|
|
|
@ -8,7 +8,6 @@ import Locale from "./i18n/Locale";
|
||||||
import State from "../State";
|
import State from "../State";
|
||||||
|
|
||||||
import {UIEventSource} from "../Logic/UIEventSource";
|
import {UIEventSource} from "../Logic/UIEventSource";
|
||||||
import {Img} from "./Img";
|
|
||||||
import Svg from "../Svg";
|
import Svg from "../Svg";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -115,8 +114,8 @@ export class SimpleAddUI extends UIElement {
|
||||||
|
|
||||||
const loc = State.state.bm.LastClickLocation.data;
|
const loc = State.state.bm.LastClickLocation.data;
|
||||||
let feature = State.state.changes.createElement(tags, loc.lat, loc.lon);
|
let feature = State.state.changes.createElement(tags, loc.lat, loc.lon);
|
||||||
layerToAddTo.AddNewElement(feature);
|
|
||||||
State.state.selectedElement.setData(feature);
|
State.state.selectedElement.setData(feature);
|
||||||
|
layerToAddTo.AddNewElement(feature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,27 +40,27 @@
|
||||||
id="namedview8"
|
id="namedview8"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
inkscape:zoom="1.5733334"
|
inkscape:zoom="1.5733334"
|
||||||
inkscape:cx="84.526201"
|
inkscape:cx="-20.982269"
|
||||||
inkscape:cy="188.2981"
|
inkscape:cy="188.2981"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="0"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="svg6" />
|
inkscape:current-layer="svg6" />
|
||||||
<path
|
<path
|
||||||
style="fill:#ffffff;stroke:#ffffff;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill-opacity:1"
|
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
d="M 34.759205,76.188657 H 262.93718 c 37.50409,0.111509 38.67272,47.104633 0,46.615173 H 34.759205 c -33.95631907,0.10039 -33.5365026,-46.998068 0,-46.615173 z"
|
d="M 34.759205,66.019166 H 262.93718 c 37.50409,0.111509 38.67272,47.104634 0,46.615174 H 34.759205 c -33.95631907,0.10039 -33.5365026,-46.998069 0,-46.615174 z"
|
||||||
id="path818"
|
id="path818"
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
sodipodi:nodetypes="ccccc" />
|
sodipodi:nodetypes="ccccc" />
|
||||||
<path
|
<path
|
||||||
style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
d="M 49.750453,132.33688 249.19481,132.00969 C 248.9489,264.76342 49.140118,261.25015 49.750453,132.33688 Z"
|
d="M 49.750453,122.23061 249.19481,121.8402 C 248.9489,280.24546 49.140118,276.05333 49.750453,122.23061 Z"
|
||||||
id="path823"
|
id="path823"
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
sodipodi:nodetypes="ccc" />
|
sodipodi:nodetypes="ccc" />
|
||||||
<g
|
<g
|
||||||
id="g848"
|
id="g848"
|
||||||
transform="matrix(1.4204816,0,0,1.4204816,-61.951413,-43.532931)"
|
transform="matrix(1.4204816,0,0,1.4204816,-61.951413,-40.990558)"
|
||||||
style="stroke-width:0.70398653">
|
style="stroke-width:0.70398653">
|
||||||
<ellipse
|
<ellipse
|
||||||
ry="23.81991"
|
ry="23.81991"
|
||||||
|
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
@ -43,10 +43,15 @@ Contains tweaks for small screens
|
||||||
#geolocate-button {
|
#geolocate-button {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.leaflet-popup {
|
.leaflet-popup {
|
||||||
/* Popups are hidden on mobile */
|
transform: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-popup-content {
|
||||||
|
/* On mobile, the popups are shown as a full-screen element */
|
||||||
display: none;
|
display: none;
|
||||||
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#centermessage {
|
#centermessage {
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
--background-color: white;
|
--background-color: white;
|
||||||
--foreground-color: black;
|
--foreground-color: black;
|
||||||
--popup-border: white;
|
--popup-border: white;
|
||||||
--shadow-color: #00000066
|
--shadow-color: #00000066;
|
||||||
|
|
||||||
|
--return-to-the-map-height: 5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
"deploy:staging": "npm run prepare-deploy && rm -rf /home/pietervdvn/git/pietervdvn.github.io/Staging/* && cp -r dist/* /home/pietervdvn/git/pietervdvn.github.io/Staging/ && cd /home/pietervdvn/git/pietervdvn.github.io/ && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean",
|
"deploy:staging": "npm run prepare-deploy && rm -rf /home/pietervdvn/git/pietervdvn.github.io/Staging/* && cp -r dist/* /home/pietervdvn/git/pietervdvn.github.io/Staging/ && cd /home/pietervdvn/git/pietervdvn.github.io/ && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean",
|
||||||
"deploy:production": "npm run prepare-deploy && npm run optimize-images && rm -rf /home/pietervdvn/git/pietervdvn.github.io/MapComplete/* && cp -r dist/* /home/pietervdvn/git/pietervdvn.github.io/MapComplete/ && cd /home/pietervdvn/git/pietervdvn.github.io/ && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean",
|
"deploy:production": "npm run prepare-deploy && npm run optimize-images && rm -rf /home/pietervdvn/git/pietervdvn.github.io/MapComplete/* && cp -r dist/* /home/pietervdvn/git/pietervdvn.github.io/MapComplete/ && cd /home/pietervdvn/git/pietervdvn.github.io/ && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean",
|
||||||
|
|
||||||
"clean": "rm *.webmanifest && find *.html | grep -v \"\\(index\\|land\\|test\\|preferences\\|customGenerator\\).html\" | xargs rm"
|
"clean": "rm -rf .cache/ && (find *.html | grep -v \"\\(index\\|land\\|test\\|preferences\\|customGenerator\\).html\" | xargs rm) && (find *.webmanifest | xargs rm)"
|
||||||
|
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|
Loading…
Reference in a new issue