diff --git a/Customizations/JSON/FromJSON.ts b/Customizations/JSON/FromJSON.ts
index 949288a..ed66275 100644
--- a/Customizations/JSON/FromJSON.ts
+++ b/Customizations/JSON/FromJSON.ts
@@ -11,7 +11,7 @@ export class FromJSON {
}
public static Tag(json: AndOrTagConfigJson | string, context: string = ""): TagsFilter {
- if(json === undefined){
+ if (json === undefined) {
throw `Error while parsing a tag: 'json' is undefined in ${context}. Make sure all the tags are defined and at least one tag is present in a complex expression`
}
if (typeof (json) == "string") {
@@ -33,7 +33,7 @@ export class FromJSON {
split[1] = "..*"
}
return new RegexTag(
- new RegExp("^" + split[0] + "$"),
+ new RegExp("^" + split[0] + "$"),
new RegExp("^" + split[1] + "$")
);
}
@@ -58,11 +58,17 @@ export class FromJSON {
new RegExp("^" + split[1] + "$")
);
}
- const split = Utils.SplitFirst(tag, "=");
- if(split[1] == "*"){
- throw `Error while parsing tag '${tag}' in ${context}: detected a wildcard on a normal value. Use a regex pattern instead`
+ if (tag.indexOf("=") >= 0) {
+
+
+ const split = Utils.SplitFirst(tag, "=");
+ if (split[1] == "*") {
+ throw `Error while parsing tag '${tag}' in ${context}: detected a wildcard on a normal value. Use a regex pattern instead`
+ }
+ return new Tag(split[0], split[1])
}
- return new Tag(split[0], split[1])
+ throw `Error while parsing tag '${tag}' in ${context}: no key part and value part were found`
+
}
if (json.and !== undefined) {
return new And(json.and.map(t => FromJSON.Tag(t, context)));
diff --git a/Customizations/JSON/LayerConfig.ts b/Customizations/JSON/LayerConfig.ts
index 8cd20fd..42c0936 100644
--- a/Customizations/JSON/LayerConfig.ts
+++ b/Customizations/JSON/LayerConfig.ts
@@ -253,20 +253,13 @@ export default class LayerConfig {
let sourceParts = iconUrl.split(";");
function genHtmlFromString(sourcePart: string): UIElement {
- const style = `width:100%;height:100%;rotate:${rotation};display:block;position: absolute; top: 0, left: 0`;
+ const style = `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0, left: 0`;
let html: UIElement = new FixedUiElement(``);
- const match = sourcePart.match(/([a-zA-Z0-9_]*):#([0-9a-fA-F]{3,6})/)
+ const match = sourcePart.match(/([a-zA-Z0-9_]*):([^;]*)/)
if (match !== null && Svg.All[match[1] + ".svg"] !== undefined) {
html = new Combine([
(Svg.All[match[1] + ".svg"] as string)
- .replace(/#000000/g, "#" + match[2])
- ]).SetStyle(style);
- }
-
- if (sourcePart.startsWith(Utils.assets_path)) {
- const key = sourcePart.substr(Utils.assets_path.length);
- html = new Combine([
- (Svg.All[key] as string).replace(/stop-color:#000000/g, 'stop-color:' + color)
+ .replace(/#000000/g, match[2])
]).SetStyle(style);
}
return html;
diff --git a/InitUiElements.ts b/InitUiElements.ts
index fbaa371..9193142 100644
--- a/InitUiElements.ts
+++ b/InitUiElements.ts
@@ -1,53 +1,45 @@
-import Translations from "./UI/i18n/Translations";
-import {TabbedComponent} from "./UI/Base/TabbedComponent";
-import {ShareScreen} from "./UI/ShareScreen";
import {FixedUiElement} from "./UI/Base/FixedUiElement";
import CheckBox from "./UI/Input/CheckBox";
import Combine from "./UI/Base/Combine";
-import {UIElement} from "./UI/UIElement";
-import {MoreScreen} from "./UI/MoreScreen";
-import {FilteredLayer} from "./Logic/FilteredLayer";
-import {Basemap} from "./UI/Basemap";
+import {Basemap} from "./UI/BigComponents/Basemap";
import State from "./State";
-import {WelcomeMessage} from "./UI/WelcomeMessage";
-import {LayerSelection} from "./UI/LayerSelection";
-import {VariableUiElement} from "./UI/Base/VariableUIElement";
import LoadFromOverpass from "./Logic/Actors/UpdateFromOverpass";
import {UIEventSource} from "./Logic/UIEventSource";
import {QueryParameters} from "./Logic/Web/QueryParameters";
-import {PersonalLayersPanel} from "./UI/PersonalLayersPanel";
-import Locale from "./UI/i18n/Locale";
-import {StrayClickHandler} from "./Logic/Actors/StrayClickHandler";
-import {SimpleAddUI} from "./UI/SimpleAddUI";
-import {CenterMessageBox} from "./UI/CenterMessageBox";
+import StrayClickHandler from "./Logic/Actors/StrayClickHandler";
+import SimpleAddUI from "./UI/BigComponents/SimpleAddUI";
+import CenterMessageBox from "./UI/CenterMessageBox";
import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
import {TagUtils} from "./Logic/Tags";
-import {UserBadge} from "./UI/UserBadge";
-import {SearchAndGo} from "./UI/SearchAndGo";
-import {FullScreenMessageBox} from "./UI/FullScreenMessageBoxHandler";
-import {GeoLocationHandler} from "./Logic/Actors/GeoLocationHandler";
+import UserBadge from "./UI/BigComponents/UserBadge";
+import SearchAndGo from "./UI/BigComponents/SearchAndGo";
+import FullScreenMessageBox from "./UI/FullScreenMessageBoxHandler";
+import GeoLocationHandler from "./Logic/Actors/GeoLocationHandler";
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
import {Utils} from "./Utils";
-import BackgroundSelector from "./UI/BackgroundSelector";
-import {FeatureInfoBox} from "./UI/Popup/FeatureInfoBox";
+import FeatureInfoBox from "./UI/Popup/FeatureInfoBox";
import Svg from "./Svg";
import Link from "./UI/Base/Link";
import * as personal from "./assets/themes/personalLayout/personalLayout.json"
import LayoutConfig from "./Customizations/JSON/LayoutConfig";
import * as L from "leaflet";
import Img from "./UI/Base/Img";
-import {UserDetails} from "./Logic/Osm/OsmConnection";
-import Attribution from "./UI/Misc/Attribution";
-import Constants from "./Models/Constants";
+import UserDetails from "./Logic/Osm/OsmConnection";
+import Attribution from "./UI/BigComponents/Attribution";
import MetaTagging from "./Logic/MetaTagging";
import FeatureSourceMerger from "./Logic/FeatureSource/FeatureSourceMerger";
import RememberingSource from "./Logic/FeatureSource/RememberingSource";
import FilteringFeatureSource from "./Logic/FeatureSource/FilteringFeatureSource";
import WayHandlingApplyingFeatureSource from "./Logic/FeatureSource/WayHandlingApplyingFeatureSource";
-import FeatureSource from "./Logic/FeatureSource/FeatureSource";
import NoOverlapSource from "./Logic/FeatureSource/NoOverlapSource";
import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers";
import LayerResetter from "./Logic/Actors/LayerResetter";
+import FullWelcomePaneWithTabs from "./UI/BigComponents/FullWelcomePaneWithTabs";
+import LayerControlPanel from "./UI/BigComponents/LayerControlPanel";
+import FeatureSwitched from "./UI/Base/FeatureSwitched";
+import FeatureDuplicatorPerLayer from "./Logic/FeatureSource/FeatureDuplicatorPerLayer";
+import LayerConfig from "./Customizations/JSON/LayerConfig";
+import ShowDataLayer from "./UI/ShowDataLayer";
export class InitUiElements {
@@ -87,10 +79,6 @@ export class InitUiElements {
}
-
-
-
-
InitUiElements.InitBaseMap();
new FixedUiElement("").AttachTo("decoration-desktop"); // Remove the decoration
@@ -216,13 +204,15 @@ export class InitUiElements {
marker.addTo(State.state.leafletMap.data)
});
- new GeoLocationHandler(
- State.state.currentGPSLocation,
- State.state.leafletMap,
- State.state.featureSwitchGeolocation
- )
- .SetStyle(`position:relative;display:block;border: solid 2px #0005;cursor: pointer; z-index: 999; /*Just below leaflets zoom*/background-color: white;border-radius: 5px;width: 43px;height: 43px;`)
+ new FeatureSwitched(
+ new GeoLocationHandler(
+ State.state.currentGPSLocation,
+ State.state.leafletMap
+ )
+ .SetStyle(`position:relative;display:block;border: solid 2px #0005;cursor: pointer; z-index: 999; /*Just below leaflets zoom*/background-color: white;border-radius: 5px;width: 43px;height: 43px;`)
+ , State.state.featureSwitchGeolocation)
.AttachTo("geolocate-button");
+
State.state.locationControl.ping();
}
@@ -254,23 +244,18 @@ export class InitUiElements {
}
}
- static OnlyIf(featureSwitch: UIEventSource, callback: () => void) {
- featureSwitch.addCallback(() => {
+ private static OnlyIf(featureSwitch: UIEventSource, callback: () => void) {
+ featureSwitch.addCallbackAndRun(() => {
if (featureSwitch.data) {
callback();
}
});
-
- if (featureSwitch.data) {
- callback();
- }
-
}
- static InitWelcomeMessage() {
+ private static InitWelcomeMessage() {
- const fullOptions = this.CreateWelcomePane();
+ const fullOptions = new FullWelcomePaneWithTabs();
const help = Svg.help_svg().SetClass("open-welcome-button");
const close = Svg.close_svg().SetClass("close-welcome-button");
@@ -298,7 +283,7 @@ export class InitUiElements {
})
- const fullOptions2 = this.CreateWelcomePane();
+ const fullOptions2 = new FullWelcomePaneWithTabs();
State.state.fullScreenMessage.setData(fullOptions2)
Svg.help_svg()
@@ -311,15 +296,11 @@ export class InitUiElements {
}
- static InitLayerSelection() {
+ private static InitLayerSelection() {
InitUiElements.OnlyIf(State.state.featureSwitchLayers, () => {
- const layerControlPanel = this.GenerateLayerControlPanel();
- if (layerControlPanel === undefined) {
- return;
- }
-
- layerControlPanel.SetStyle("display:block;padding:0.75em;border-radius:1em;");
+ const layerControlPanel = new LayerControlPanel()
+ .SetStyle("display:block;padding:0.75em;border-radius:1em;");
const closeButton = Svg.close_svg().SetClass("layer-selection-toggle").SetStyle(" background: var(--subtle-detail-color);")
const checkbox = new CheckBox(
new Combine([
@@ -334,18 +315,19 @@ export class InitUiElements {
checkbox.AttachTo("layer-selection");
- State.state.locationControl.addCallback(() => {
+ State.state.locationControl
+ .addCallback(() => {
// Close the layer selection when the map is moved
- checkbox.isEnabled.setData(false);
+ // checkbox.isEnabled.setData(false);
});
- const fullScreen = this.GenerateLayerControlPanel();
+ const fullScreen = new LayerControlPanel();
checkbox.isEnabled.addCallback(isEnabled => {
if (isEnabled) {
State.state.fullScreenMessage.setData(fullScreen);
}
})
- State.state.fullScreenMessage.addCallbackAndRun(latest => {
+ State.state.fullScreenMessage.addCallback(latest => {
if (latest === undefined) {
checkbox.isEnabled.setData(false);
}
@@ -354,7 +336,7 @@ export class InitUiElements {
});
}
- static InitBaseMap() {
+ private static InitBaseMap() {
State.state.availableBackgroundLayers = new AvailableBaseLayers(State.state.locationControl).availableEditorLayers;
State.state.backgroundLayer = QueryParameters.GetQueryParameter("background",
@@ -371,8 +353,6 @@ export class InitUiElements {
}, [], layer => layer.id);
-
-
new LayerResetter(
State.state.backgroundLayer, State.state.locationControl,
State.state.availableBackgroundLayers, State.state.layoutToUse.map((layout: LayoutConfig) => layout.defaultBackgroundId));
@@ -393,61 +373,33 @@ export class InitUiElements {
}
- static InitLayers() {
-
-
+ private static InitLayers() {
const state = State.state;
- const flayers: FilteredLayer[] = []
+ const flayers: { layerDef: LayerConfig, isDisplayed: UIEventSource }[] = []
for (const layer of state.layoutToUse.data.layers) {
if (typeof (layer) === "string") {
throw "Layer " + layer + " was not substituted";
}
- let generateContents = (tags: UIEventSource) => new FeatureInfoBox(tags, layer);
- if (layer.title === undefined && (layer.tagRenderings ?? []).length === 0) {
- generateContents = undefined;
+ const isDisplayed = QueryParameters.GetQueryParameter("layer-" + layer.id, "true", "Wether or not layer " + layer.id + " is shown")
+ .map((str) => str !== "false", [], (b) => b.toString());
+ const flayer = {
+ isDisplayed: isDisplayed,
+ layerDef: layer
}
-
- const flayer: FilteredLayer = new FilteredLayer(layer, generateContents);
flayers.push(flayer);
-
- QueryParameters.GetQueryParameter("layer-" + layer.id, "true", "Wether or not layer " + layer.id + " is shown")
- .map((str) => str !== "false", [], (b) => b.toString())
- .syncWith(
- flayer.isDisplayed
- )
}
State.state.filteredLayers.setData(flayers);
- function addMatchingIds(src: FeatureSource) {
-
- src.features.addCallback(features => {
- features.forEach(f => {
- const properties = f.feature.properties;
- if (properties._matching_layer_id) {
- return;
- }
-
- for (const flayer of flayers) {
- if (flayer.layerDef.overpassTags.matchesProperties(properties)) {
- properties._matching_layer_id = flayer.layerDef.id;
- break;
- }
- }
- })
- });
- }
+
const updater = new LoadFromOverpass(state.locationControl, state.layoutToUse, state.leafletMap);
State.state.layerUpdater = updater;
-
- addMatchingIds(updater);
- addMatchingIds(State.state.changes);
-
+
const source =
new FilteringFeatureSource(
@@ -455,9 +407,9 @@ export class InitUiElements {
State.state.locationControl,
new FeatureSourceMerger([
new RememberingSource(new WayHandlingApplyingFeatureSource(flayers,
- new NoOverlapSource(flayers, updater)
+ new NoOverlapSource(flayers, new FeatureDuplicatorPerLayer(flayers, updater))
)),
- State.state.changes]));
+ new FeatureDuplicatorPerLayer(flayers, State.state.changes)]));
source.features.addCallback((featuresFreshness: { feature: any, freshness: Date }[]) => {
@@ -466,25 +418,9 @@ export class InitUiElements {
State.state.allElements.addElement(feature);
})
MetaTagging.addMetatags(features);
-
- function renderLayers(layers) {
-
-
- if (layers.length === 0) {
- if (features.length > 0) {
- console.warn("Got some leftovers: ", features.join("; "))
- }
- return;
- }
- const layer = layers[0];
- const rest = layers.slice(1, layers.length);
- features = layer.SetApplicableData(features);
- renderLayers(rest);
- }
-
- renderLayers(flayers);
-
})
+
+ new ShowDataLayer(source.features, State.state.leafletMap, flayers);
}
@@ -528,72 +464,4 @@ export class InitUiElements {
new CenterMessageBox().AttachTo("centermessage");
}
-
- private static CreateWelcomePane() {
-
- const layoutToUse = State.state.layoutToUse.data;
- let welcome: UIElement = new WelcomeMessage();
- if (layoutToUse.id === personal.id) {
- welcome = new PersonalLayersPanel();
- }
-
- const tabs = [
- {header: ``, content: welcome},
- {
- header: Svg.osm_logo_img,
- content: Translations.t.general.openStreetMapIntro as UIElement
- },
-
- ]
-
- if (State.state.featureSwitchShareScreen.data) {
- tabs.push({header: Svg.share_img, content: new ShareScreen()});
- }
-
- if (State.state.featureSwitchMoreQuests.data) {
-
- tabs.push({
- header: Svg.add_img,
- content: new MoreScreen()
- });
- }
-
-
- tabs.push({
- header: Svg.help,
- content: new VariableUiElement(State.state.osmConnection.userDetails.map(userdetails => {
- if (userdetails.csCount < Constants.userJourney.mapCompleteHelpUnlock) {
- return ""
- }
- return new Combine([Translations.t.general.aboutMapcomplete, "
Version " + Constants.vNumber]).Render();
- }, [Locale.language]))
- }
- );
-
-
- return new TabbedComponent(tabs, State.state.welcomeMessageOpenedTab)
- .ListenTo(State.state.osmConnection.userDetails);
-
- }
-
- private static GenerateLayerControlPanel() {
-
-
- let layerControlPanel: UIElement = undefined;
- if (State.state.layoutToUse.data.enableBackgroundLayerSelection) {
- layerControlPanel = new BackgroundSelector();
- layerControlPanel.SetStyle("margin:1em");
- layerControlPanel.onClick(() => {
- });
- }
-
- if (State.state.filteredLayers.data.length > 1) {
- const layerSelection = new LayerSelection();
- layerSelection.onClick(() => {
- });
- layerControlPanel = new Combine([layerSelection, "
", layerControlPanel]);
- }
- return layerControlPanel;
- }
-
}
\ No newline at end of file
diff --git a/Logic/Actors/GeoLocationHandler.ts b/Logic/Actors/GeoLocationHandler.ts
index a8d2ec2..688c14a 100644
--- a/Logic/Actors/GeoLocationHandler.ts
+++ b/Logic/Actors/GeoLocationHandler.ts
@@ -5,7 +5,7 @@ import {Utils} from "../../Utils";
import Svg from "../../Svg";
import Img from "../../UI/Base/Img";
-export class GeoLocationHandler extends UIElement {
+export default class GeoLocationHandler extends UIElement {
private readonly _isActive: UIEventSource = new UIEventSource(false);
private readonly _permission: UIEventSource = new UIEventSource("");
@@ -13,17 +13,14 @@ export class GeoLocationHandler extends UIElement {
private readonly _hasLocation: UIEventSource;
private readonly _currentGPSLocation: UIEventSource<{ latlng: any; accuracy: number }>;
private readonly _leafletMap: UIEventSource;
- private readonly _featureSwitch: UIEventSource;
constructor(currentGPSLocation: UIEventSource<{ latlng: any; accuracy: number }>,
- leafletMap: UIEventSource,
- featureSwitch: UIEventSource) {
+ leafletMap: UIEventSource) {
super(undefined);
this._currentGPSLocation = currentGPSLocation;
this._leafletMap = leafletMap;
- this._featureSwitch = featureSwitch;
this._hasLocation = currentGPSLocation.map((location) => location !== undefined);
- var self = this;
+ const self = this;
import("../../vendor/Leaflet.AccuratePosition.js").then(() => {
self.init();
})
@@ -92,10 +89,6 @@ export class GeoLocationHandler extends UIElement {
}
InnerRender(): string {
- if (!this._featureSwitch.data) {
- return "";
- }
-
if (this._hasLocation.data) {
return Svg.crosshair_blue_img;
}
@@ -124,7 +117,7 @@ export class GeoLocationHandler extends UIElement {
private StartGeolocating(zoomlevel = 19) {
const self = this;
- const map : any = this._leafletMap.data;
+ const map: any = this._leafletMap.data;
if (self._permission.data === "denied") {
return "";
}
diff --git a/Logic/Actors/StrayClickHandler.ts b/Logic/Actors/StrayClickHandler.ts
index 558a0d2..a7041d6 100644
--- a/Logic/Actors/StrayClickHandler.ts
+++ b/Logic/Actors/StrayClickHandler.ts
@@ -8,14 +8,14 @@ import Img from "../../UI/Base/Img";
* The stray-click-hanlders adds a marker to the map if no feature was clicked.
* Shows the given uiToShow-element in the messagebox
*/
-export class StrayClickHandler {
+export default class StrayClickHandler {
private _lastMarker;
private _uiToShow: (() => UIElement);
constructor(
- lastClickLocation: UIEventSource<{ lat: number, lon:number }>,
+ lastClickLocation: UIEventSource<{ lat: number, lon: number }>,
selectedElement: UIEventSource,
- filteredLayers: UIEventSource<{ readonly isDisplayed: UIEventSource}[]>,
+ filteredLayers: UIEventSource<{ readonly isDisplayed: UIEventSource }[]>,
leafletMap: UIEventSource,
fullscreenMessage: UIEventSource,
uiToShow: (() => UIElement)) {
@@ -23,14 +23,14 @@ export class StrayClickHandler {
const self = this;
filteredLayers.data.forEach((filteredLayer) => {
filteredLayer.isDisplayed.addCallback(isEnabled => {
- if(isEnabled && self._lastMarker && leafletMap.data !== undefined){
+ if (isEnabled && self._lastMarker && leafletMap.data !== undefined) {
// When a layer is activated, we remove the 'last click location' in order to force the user to reclick
// This reclick might be at a location where a feature now appeared...
- leafletMap.data.removeLayer(self._lastMarker);
+ leafletMap.data.removeLayer(self._lastMarker);
}
})
})
-
+
lastClickLocation.addCallback(function (lastClick) {
selectedElement.setData(undefined);
diff --git a/Logic/Actors/UpdateFromOverpass.ts b/Logic/Actors/UpdateFromOverpass.ts
index ab4e95b..63067b5 100644
--- a/Logic/Actors/UpdateFromOverpass.ts
+++ b/Logic/Actors/UpdateFromOverpass.ts
@@ -38,6 +38,7 @@ export default class UpdateFromOverpass implements FeatureSource{
location: UIEventSource,
layoutToUse: UIEventSource,
leafletMap: UIEventSource) {
+ console.log("Crating overpass updater")
this._location = location;
this._layoutToUse = layoutToUse;
this._leafletMap = leafletMap;
diff --git a/Logic/FeatureSource/FeatureDuplicatorPerLayer.ts b/Logic/FeatureSource/FeatureDuplicatorPerLayer.ts
new file mode 100644
index 0000000..b27ba2a
--- /dev/null
+++ b/Logic/FeatureSource/FeatureDuplicatorPerLayer.ts
@@ -0,0 +1,68 @@
+import FeatureSource from "./FeatureSource";
+import {UIEventSource} from "../UIEventSource";
+import LayerConfig from "../../Customizations/JSON/LayerConfig";
+
+
+/**
+ * In some rare cases, some elements are shown on multiple layers (when 'passthrough' is enabled)
+ * If this is the case, multiple objects with a different _matching_layer_id are generated.
+ * If not, the _feature_layter_id is added
+ */
+export default class FeatureDuplicatorPerLayer implements FeatureSource {
+ public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>;
+
+
+ constructor(layers: { layerDef: LayerConfig }[], upstream: FeatureSource) {
+ let noPassthroughts = true;
+ for (const layer of layers) {
+ if (layer.layerDef.passAllFeatures) {
+ noPassthroughts = false;
+ break;
+ }
+ }
+
+ this.features = upstream.features.map(features => {
+ const newFeatures: { feature: any, freshness: Date }[] = [];
+ if(features === undefined){
+ return newFeatures;
+ }
+
+
+ for (const f of features) {
+ if (f.feature._matching_layer_id) {
+ // Already matched previously
+ // We simply add it
+ newFeatures.push(f);
+ return;
+ }
+
+ for (const layer of layers) {
+ if (layer.layerDef.overpassTags.matchesProperties(f.feature.properties)) {
+ if (layer.layerDef.passAllFeatures) {
+
+ // We copy the feature; the "properties" field is kept identical though!
+ // Keeping "properties" identical is needed, as it might break the 'allElementStorage' otherwise
+ const newFeature = {
+ geometry: f.feature.geometry,
+ id: f.feature.id,
+ type: f.feature.type,
+ properties: f.feature.properties,
+ _matching_layer_id : layer.layerDef.id
+ }
+ newFeatures.push({feature: newFeature, freshness: f.freshness});
+ } else {
+ // If not 'passAllFeatures', we are done
+ f.feature._matching_layer_id = layer.layerDef.id;
+ newFeatures.push(f);
+ break;
+ }
+ }
+ }
+ }
+ return newFeatures;
+
+ })
+
+ }
+
+}
\ No newline at end of file
diff --git a/Logic/FeatureSource/FeatureSourceMerger.ts b/Logic/FeatureSource/FeatureSourceMerger.ts
index 1755aef..2ea08c4 100644
--- a/Logic/FeatureSource/FeatureSourceMerger.ts
+++ b/Logic/FeatureSource/FeatureSourceMerger.ts
@@ -18,7 +18,7 @@ export default class FeatureSourceMerger implements FeatureSource {
let all = {}; // Mapping 'id' -> {feature, freshness}
for (const source of this._sources) {
for (const f of source.features.data) {
- const id = f.feature.properties.id+f.feature.geometry.type;
+ const id = f.feature.properties.id+f.feature.geometry.type+f.feature._matching_layer_id;
const oldV = all[id];
if(oldV === undefined){
all[id] = f;
diff --git a/Logic/FeatureSource/FilteringFeatureSource.ts b/Logic/FeatureSource/FilteringFeatureSource.ts
index f3317aa..843ca28 100644
--- a/Logic/FeatureSource/FilteringFeatureSource.ts
+++ b/Logic/FeatureSource/FilteringFeatureSource.ts
@@ -14,14 +14,13 @@ export default class FilteringFeatureSource implements FeatureSource {
upstream: FeatureSource) {
const layerDict = {};
-
+
const self = this;
-
+
function update() {
- console.log("UPdating...")
const features: { feature: any, freshness: Date }[] = upstream.features.data;
const newFeatures = features.filter(f => {
- const layerId = f.feature.properties._matching_layer_id;
+ const layerId = f.feature._matching_layer_id;
if (layerId === undefined) {
console.error(f)
throw "feature._matching_layer_id is undefined"
@@ -37,16 +36,22 @@ export default class FilteringFeatureSource implements FeatureSource {
});
self.features.setData(newFeatures);
}
+
for (const layer of layers) {
layerDict[layer.layerDef.id] = layer;
- layer.isDisplayed.addCallback(update)
+ layer.isDisplayed.addCallback(() => {
+ console.log("Updating due to layer change")
+ update()})
}
- upstream.features.addCallback(update);
- location.map(l => l.zoom).addCallback(update);
+ upstream.features.addCallback(() => {
+ console.log("Updating due to upstream change")
+ update()});
+ location.map(l => l.zoom).addCallback(() => {
+ console.log("UPdating due to zoom level change")
+ update();});
}
-
}
\ No newline at end of file
diff --git a/Logic/FeatureSource/NoOverlapSource.ts b/Logic/FeatureSource/NoOverlapSource.ts
index 9dd6681..73d69de 100644
--- a/Logic/FeatureSource/NoOverlapSource.ts
+++ b/Logic/FeatureSource/NoOverlapSource.ts
@@ -45,7 +45,7 @@ export default class NoOverlapSource {
partitions[layerId] = []
}
for (const feature of features) {
- partitions[feature.feature.properties._matching_layer_id].push(feature);
+ partitions[feature.feature._matching_layer_id].push(feature);
}
// With this partitioning in hand, we run over every layer and remove every underlying feature if needed
diff --git a/Logic/FeatureSource/WayHandlingApplyingFeatureSource.ts b/Logic/FeatureSource/WayHandlingApplyingFeatureSource.ts
index 170e434..66faccf 100644
--- a/Logic/FeatureSource/WayHandlingApplyingFeatureSource.ts
+++ b/Logic/FeatureSource/WayHandlingApplyingFeatureSource.ts
@@ -32,7 +32,7 @@ export default class WayHandlingApplyingFeatureSource implements FeatureSource {
const newFeatures: { feature: any, freshness: Date }[] = [];
for (const f of features) {
const feat = f.feature;
- const layerId = feat.properties._matching_layer_id;
+ const layerId = feat._matching_layer_id;
const layer: LayerConfig = layerDict[layerId].layerDef;
if (layer === undefined) {
throw "No layer found with id " + layerId;
@@ -50,6 +50,7 @@ export default class WayHandlingApplyingFeatureSource implements FeatureSource {
}
const centerPoint = GeoOperations.centerpoint(feat);
+ centerPoint._matching_layer_id = feat._matching_layer_id;
newFeatures.push({feature: centerPoint, freshness: f.freshness});
if(layer.wayHandling === LayerConfig.WAYHANDLING_CENTER_AND_WAY){
diff --git a/Logic/FilteredLayer.ts b/Logic/FilteredLayer.ts
deleted file mode 100644
index ed2f9b0..0000000
--- a/Logic/FilteredLayer.ts
+++ /dev/null
@@ -1,163 +0,0 @@
-import {TagsFilter, TagUtils} from "./Tags";
-import {UIEventSource} from "./UIEventSource";
-import * as L from "leaflet"
-import {Layer} from "leaflet"
-import {GeoOperations} from "./GeoOperations";
-import {UIElement} from "../UI/UIElement";
-import State from "../State";
-import LayerConfig from "../Customizations/JSON/LayerConfig";
-import Hash from "./Web/Hash";
-import LazyElement from "../UI/Base/LazyElement";
-
-/***
- *
- */
-export class FilteredLayer {
-
- public readonly name: string | UIElement;
- public readonly isDisplayed: UIEventSource = new UIEventSource(true);
- public readonly layerDef: LayerConfig;
-
- private readonly filters: TagsFilter;
- private readonly _maxAllowedOverlap: number;
-
- /** The featurecollection from overpass
- */
- private _dataFromOverpass: any[];
- /**
- * The leaflet layer object which should be removed on rerendering
- */
- private _geolayer;
-
- private _showOnPopup: (tags: UIEventSource, feature: any) => UIElement;
-
-
- constructor(
- layerDef: LayerConfig,
- showOnPopup: ((tags: UIEventSource, feature: any) => UIElement)
- ) {
- this.layerDef = layerDef;
-
- this._showOnPopup = showOnPopup;
- this.name = name;
- this.filters = layerDef.overpassTags;
- this._maxAllowedOverlap = layerDef.hideUnderlayingFeaturesMinPercentage;
-
- }
-
- /**
- * 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
- */
- public SetApplicableData(features: any[]): any[] {
- const leftoverFeatures = [];
- const selfFeatures = [];
- for (let feature of features) {
- const tags = TagUtils.proprtiesToKV(feature.properties);
- const matches = this.filters.matches(tags);
- if (matches) {
- selfFeatures.push(feature);
- }
- if (!matches || this.layerDef.passAllFeatures) {
- leftoverFeatures.push(feature);
- }
- }
-
- this.RenderLayer(selfFeatures)
- return leftoverFeatures;
- }
-
-
- private RenderLayer(features: any[]) {
-
- if (this._geolayer !== undefined && this._geolayer !== null) {
- // Remove the old geojson layer from the map - we'll reshow all the elements later on anyway
- State.state.leafletMap.data.removeLayer(this._geolayer);
- }
-
- // We fetch all the data we have to show:
- const data = {
- type: "FeatureCollection",
- features: features
- }
-
- let self = this;
- this._geolayer = L.geoJSON(data, {
- style: feature => {
- const tagsSource = State.state.allElements.getEventSourceFor(feature);
- return self.layerDef.GenerateLeafletStyle(tagsSource, self._showOnPopup !== undefined);
- },
- 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 tagSource = State.state.allElements.getEventSourceFor(feature);
-
- const style = self.layerDef.GenerateLeafletStyle(tagSource, self._showOnPopup !== undefined);
- let marker;
- if (style.icon === undefined) {
- marker = L.circle(latLng, {
- radius: 25,
- color: style.color
- });
- } else {
- marker = L.marker(latLng, {
- icon: L.divIcon({
- html: style.icon.html.Render(),
- className: style.icon.className,
- iconAnchor: style.icon.iconAnchor,
- iconUrl: style.icon.iconUrl,
- popupAnchor: style.icon.popupAnchor,
- iconSize: style.icon.iconSize
- })
- });
- }
- return marker;
- },
- onEachFeature: function (feature, layer: Layer) {
-
- if (self._showOnPopup === undefined) {
- // No popup contents defined -> don't do anything
- return;
- }
- const popup = L.popup({
- autoPan: true,
- closeOnEscapeKey: true,
- }, layer);
-
-
- const eventSource = State.state.allElements.getEventSourceFor(feature);
- let uiElement: LazyElement = new LazyElement(() => self._showOnPopup(eventSource, feature));
- popup.setContent(uiElement.Render());
- layer.bindPopup(popup);
- // We first render the UIelement (which'll still need an update later on...)
- // But at least it'll be visible already
-
-
- layer.on("click", (e) => {
- // We set the element as selected...
- uiElement.Activate();
- State.state.selectedElement.setData(feature);
- });
-
- if (feature.properties.id.replace(/\//g, "_") === Hash.Get().data) {
- // This element is in the URL, so this is a share link
- // We already open it
- uiElement.Activate();
- popup.setContent(uiElement.Render());
-
- const center = GeoOperations.centerpoint(feature).geometry.coordinates;
- popup.setLatLng({lat: center[1], lng: center[0]});
- popup.openOn(State.state.leafletMap.data);
- State.state.selectedElement.setData(feature);
- uiElement.Update();
- }
-
- }
- });
-
- this._geolayer.addTo(State.state.leafletMap.data);
-
- }
-
-
-}
\ No newline at end of file
diff --git a/Logic/Osm/OsmConnection.ts b/Logic/Osm/OsmConnection.ts
index eb6c383..1b614e8 100644
--- a/Logic/Osm/OsmConnection.ts
+++ b/Logic/Osm/OsmConnection.ts
@@ -8,7 +8,7 @@ import Svg from "../../Svg";
import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
import Img from "../../UI/Base/Img";
-export class UserDetails {
+export default class UserDetails {
public loggedIn = false;
public name = "Not logged in";
@@ -21,15 +21,15 @@ export class UserDetails {
}
export class OsmConnection {
-
+
public auth;
public userDetails: UIEventSource;
_dryRun: boolean;
public preferencesHandler: OsmPreferences;
public changesetHandler: ChangesetHandler;
-
- private _onLoggedIn : ((userDetails: UserDetails) => void)[] = [];
+
+ private _onLoggedIn: ((userDetails: UserDetails) => void)[] = [];
constructor(dryRun: boolean, oauth_token: UIEventSource,
// Used to keep multiple changesets open and to write to the correct changeset
@@ -44,11 +44,11 @@ export class OsmConnection {
} catch (e) {
console.warn("Detecting standalone mode failed", e, ". Assuming in browser and not worrying furhter")
}
-
+
const iframeMode = window !== window.top;
- if ( iframeMode || pwaStandAloneMode || !singlePage) {
+ if (iframeMode || pwaStandAloneMode || !singlePage) {
// In standalone mode, we DON'T use single page login, as 'redirecting' opens a new window anyway...
// Same for an iframe...
this.auth = new osmAuth({
@@ -74,7 +74,7 @@ export class OsmConnection {
this._dryRun = dryRun;
this.preferencesHandler = new OsmPreferences(this.auth, this);
-
+
this.changesetHandler = new ChangesetHandler(layoutName, dryRun, this, this.auth);
if (oauth_token.data !== undefined) {
console.log(oauth_token.data)
@@ -86,7 +86,7 @@ export class OsmConnection {
}, this.auth);
oauth_token.setData(undefined);
-
+
}
if (this.auth.authenticated()) {
this.AttemptLogin(); // Also updates the user badge
@@ -100,7 +100,8 @@ export class OsmConnection {
layout: LayoutConfig,
allElements: ElementStorage,
generateChangeXML: (csid: string) => string,
- continuation: () => void = () => {}) {
+ continuation: () => void = () => {
+ }) {
this.changesetHandler.UploadChangeset(layout, allElements, generateChangeXML, continuation);
}
@@ -112,10 +113,10 @@ export class OsmConnection {
return this.preferencesHandler.GetLongPreference(key, prefix);
}
- public OnLoggedIn(action: (userDetails: UserDetails) => void){
+ public OnLoggedIn(action: (userDetails: UserDetails) => void) {
this._onLoggedIn.push(action);
}
-
+
public LogOut() {
this.auth.logout();
this.userDetails.data.loggedIn = false;
@@ -132,7 +133,7 @@ export class OsmConnection {
method: 'GET',
path: '/api/0.6/user/details'
}, function (err, details) {
- if(err != null){
+ if (err != null) {
console.log(err);
return;
}
@@ -140,9 +141,9 @@ export class OsmConnection {
if (details == null) {
return;
}
-
+
self.CheckForMessagesContinuously();
-
+
// details is an XML DOM of user details
let userInfo = details.getElementsByTagName("user")[0];
@@ -177,7 +178,7 @@ export class OsmConnection {
action(self.userDetails.data);
}
self._onLoggedIn = [];
-
+
});
}
@@ -189,7 +190,7 @@ export class OsmConnection {
console.log("Checking for messages")
this.AttemptLogin();
}
- }, 5 * 60 * 1000);
+ }, 5 * 60 * 1000);
}
diff --git a/Models/BaseLayer.ts b/Models/BaseLayer.ts
index e5e1c2c..63c3abb 100644
--- a/Models/BaseLayer.ts
+++ b/Models/BaseLayer.ts
@@ -1,6 +1,6 @@
import {TileLayer} from "leaflet";
-export interface BaseLayer {
+export default interface BaseLayer {
id: string,
name: string,
layer: TileLayer,
diff --git a/State.ts b/State.ts
index 4b1c513..75329af 100644
--- a/State.ts
+++ b/State.ts
@@ -12,7 +12,7 @@ import LayoutConfig from "./Customizations/JSON/LayoutConfig";
import Hash from "./Logic/Web/Hash";
import {MangroveIdentity} from "./Logic/Web/MangroveReviews";
import InstalledThemes from "./Logic/Actors/InstalledThemes";
-import {BaseLayer} from "./Models/BaseLayer";
+import BaseLayer from "./Models/BaseLayer";
import Loc from "./Models/Loc";
import Constants from "./Models/Constants";
@@ -61,11 +61,9 @@ export default class State {
public filteredLayers: UIEventSource<{
- readonly name: string | UIElement;
readonly isDisplayed: UIEventSource,
- readonly layerDef: LayerConfig;
+ readonly layerDef: LayerConfig;
}[]> = new UIEventSource<{
- readonly name: string | UIElement;
readonly isDisplayed: UIEventSource,
readonly layerDef: LayerConfig;
}[]>([])
@@ -114,7 +112,8 @@ export default class State {
public layoutDefinition: string;
public installedThemes: UIEventSource<{ layout: LayoutConfig; definition: string }[]>;
- public layerControlIsOpened: UIEventSource = QueryParameters.GetQueryParameter("layer-control-toggle", "false", "Wether or not the layer control is shown")
+ public layerControlIsOpened: UIEventSource =
+ QueryParameters.GetQueryParameter("layer-control-toggle", "false", "Whether or not the layer control is shown")
.map((str) => str !== "false", [], b => "" + b)
public welcomeMessageOpenedTab = 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)`).map(
@@ -153,8 +152,6 @@ export default class State {
});
-
-
function featSw(key: string, deflt: (layout: LayoutConfig) => boolean, documentation: string): UIEventSource {
const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined, documentation);
// I'm so sorry about someone trying to decipher this
diff --git a/Svg.ts b/Svg.ts
index 038f80b..e37eccb 100644
--- a/Svg.ts
+++ b/Svg.ts
@@ -89,12 +89,12 @@ export default class Svg {
public static delete_icon_svg() { return new FixedUiElement(Svg.delete_icon);}
public static delete_icon_ui() { return new FixedUiElement(Svg.delete_icon_img);}
- public static direction = " "
+ public static direction = " "
public static direction_img = Img.AsImageElement(Svg.direction)
public static direction_svg() { return new FixedUiElement(Svg.direction);}
public static direction_ui() { return new FixedUiElement(Svg.direction_img);}
- public static direction_gradient = " "
+ public static direction_gradient = " "
public static direction_gradient_img = Img.AsImageElement(Svg.direction_gradient)
public static direction_gradient_svg() { return new FixedUiElement(Svg.direction_gradient);}
public static direction_gradient_ui() { return new FixedUiElement(Svg.direction_gradient_img);}
diff --git a/UI/Base/FeatureSwitched.ts b/UI/Base/FeatureSwitched.ts
new file mode 100644
index 0000000..6ff095a
--- /dev/null
+++ b/UI/Base/FeatureSwitched.ts
@@ -0,0 +1,22 @@
+import {UIElement} from "../UIElement";
+import {UIEventSource} from "../../Logic/UIEventSource";
+
+export default class FeatureSwitched extends UIElement{
+ private readonly _upstream: UIElement;
+ private readonly _swtch: UIEventSource;
+
+ constructor(upstream :UIElement,
+ swtch: UIEventSource) {
+ super(swtch);
+ this._upstream = upstream;
+ this._swtch = swtch;
+ }
+
+ InnerRender(): string {
+ if(this._swtch.data){
+ return this._upstream.Render();
+ }
+ return "";
+ }
+
+}
\ No newline at end of file
diff --git a/UI/Misc/Attribution.ts b/UI/BigComponents/Attribution.ts
similarity index 100%
rename from UI/Misc/Attribution.ts
rename to UI/BigComponents/Attribution.ts
diff --git a/UI/BackgroundSelector.ts b/UI/BigComponents/BackgroundSelector.ts
similarity index 77%
rename from UI/BackgroundSelector.ts
rename to UI/BigComponents/BackgroundSelector.ts
index b9344cd..2f58245 100644
--- a/UI/BackgroundSelector.ts
+++ b/UI/BigComponents/BackgroundSelector.ts
@@ -1,9 +1,9 @@
-import {UIElement} from "./UIElement";
-import {DropDown} from "./Input/DropDown";
-import Translations from "./i18n/Translations";
-import State from "../State";
-import {UIEventSource} from "../Logic/UIEventSource";
-import {BaseLayer} from "../Models/BaseLayer";
+import {UIElement} from "../UIElement";
+import {DropDown} from "../Input/DropDown";
+import Translations from "../i18n/Translations";
+import State from "../../State";
+import {UIEventSource} from "../../Logic/UIEventSource";
+import {BaseLayer} from "../../Models/BaseLayer";
export default class BackgroundSelector extends UIElement {
diff --git a/UI/Basemap.ts b/UI/BigComponents/Basemap.ts
similarity index 92%
rename from UI/Basemap.ts
rename to UI/BigComponents/Basemap.ts
index 3f871e6..73047a5 100644
--- a/UI/Basemap.ts
+++ b/UI/BigComponents/Basemap.ts
@@ -1,8 +1,8 @@
import * as L from "leaflet"
-import {UIEventSource} from "../Logic/UIEventSource";
-import Loc from "../Models/Loc";
-import {UIElement} from "./UIElement";
-import {BaseLayer} from "../Models/BaseLayer";
+import {UIEventSource} from "../../Logic/UIEventSource";
+import Loc from "../../Models/Loc";
+import {UIElement} from "../UIElement";
+import BaseLayer from "../../Models/BaseLayer";
export class Basemap {
diff --git a/UI/BigComponents/FullWelcomePaneWithTabs.ts b/UI/BigComponents/FullWelcomePaneWithTabs.ts
new file mode 100644
index 0000000..8dd0d80
--- /dev/null
+++ b/UI/BigComponents/FullWelcomePaneWithTabs.ts
@@ -0,0 +1,80 @@
+import {UIElement} from "../UIElement";
+import State from "../../State";
+import WelcomeMessage from "./WelcomeMessage";
+import * as personal from "../../assets/themes/personalLayout/personalLayout.json";
+import PersonalLayersPanel from "./PersonalLayersPanel";
+import Svg from "../../Svg";
+import Translations from "../i18n/Translations";
+import ShareScreen from "./ShareScreen";
+import MoreScreen from "./MoreScreen";
+import {VariableUiElement} from "../Base/VariableUIElement";
+import Constants from "../../Models/Constants";
+import Combine from "../Base/Combine";
+import Locale from "../i18n/Locale";
+import {TabbedComponent} from "../Base/TabbedComponent";
+import {UIEventSource} from "../../Logic/UIEventSource";
+import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
+import UserDetails from "../../Logic/Osm/OsmConnection";
+
+export default class FullWelcomePaneWithTabs extends UIElement {
+ private readonly _layoutToUse: UIEventSource;
+ private readonly _userDetails: UIEventSource;
+
+ private readonly _component: UIElement;
+
+ constructor() {
+ super(State.state.layoutToUse);
+ this._layoutToUse = State.state.layoutToUse;
+ this._userDetails = State.state.osmConnection.userDetails;
+
+
+ const layoutToUse = this._layoutToUse.data;
+ let welcome: UIElement = new WelcomeMessage();
+ if (layoutToUse.id === personal.id) {
+ welcome = new PersonalLayersPanel();
+ }
+ const tabs = [
+ {header: ``, content: welcome},
+ {
+ header: Svg.osm_logo_img,
+ content: Translations.t.general.openStreetMapIntro as UIElement
+ },
+
+ ]
+
+ if (State.state.featureSwitchShareScreen.data) {
+ tabs.push({header: Svg.share_img, content: new ShareScreen()});
+ }
+
+ if (State.state.featureSwitchMoreQuests.data) {
+
+ tabs.push({
+ header: Svg.add_img,
+ content: new MoreScreen()
+ });
+ }
+
+
+ tabs.push({
+ header: Svg.help,
+ content: new VariableUiElement(this._userDetails.map(userdetails => {
+ if (userdetails.csCount < Constants.userJourney.mapCompleteHelpUnlock) {
+ return ""
+ }
+ return new Combine([Translations.t.general.aboutMapcomplete, "
Version " + Constants.vNumber]).Render();
+ }, [Locale.language]))
+ }
+ );
+
+ this._component = new TabbedComponent(tabs, State.state.welcomeMessageOpenedTab)
+ .ListenTo(this._userDetails);
+
+
+ }
+
+ InnerRender(): string {
+ return this._component.Render();
+
+ }
+
+}
\ No newline at end of file
diff --git a/UI/BigComponents/LayerControlPanel.ts b/UI/BigComponents/LayerControlPanel.ts
new file mode 100644
index 0000000..8b90df0
--- /dev/null
+++ b/UI/BigComponents/LayerControlPanel.ts
@@ -0,0 +1,33 @@
+import {UIElement} from "../UIElement";
+import State from "../../State";
+import BackgroundSelector from "./BackgroundSelector";
+import LayerSelection from "./LayerSelection";
+import Combine from "../Base/Combine";
+
+export default class LayerControlPanel extends UIElement{
+ private readonly _panel: UIElement;
+
+
+ constructor() {
+ super();
+ let layerControlPanel: UIElement = undefined;
+ if (State.state.layoutToUse.data.enableBackgroundLayerSelection) {
+ layerControlPanel = new BackgroundSelector();
+ layerControlPanel.SetStyle("margin:1em");
+ layerControlPanel.onClick(() => {
+ });
+ }
+
+ if (State.state.filteredLayers.data.length > 1) {
+ const layerSelection = new LayerSelection();
+ layerSelection.onClick(() => { });
+ layerControlPanel = new Combine([layerSelection, "
", layerControlPanel]);
+ }
+ this._panel = layerControlPanel;
+ }
+
+ InnerRender(): string {
+ return this._panel.Render();
+ }
+
+}
\ No newline at end of file
diff --git a/UI/LayerSelection.ts b/UI/BigComponents/LayerSelection.ts
similarity index 77%
rename from UI/LayerSelection.ts
rename to UI/BigComponents/LayerSelection.ts
index 8406bc2..3766bdf 100644
--- a/UI/LayerSelection.ts
+++ b/UI/BigComponents/LayerSelection.ts
@@ -1,13 +1,13 @@
-import {UIElement} from "./UIElement";
-import CheckBox from "./Input/CheckBox";
-import Combine from "./Base/Combine";
-import State from "../State";
-import Translations from "./i18n/Translations";
-import {FixedUiElement} from "./Base/FixedUiElement";
-import {VariableUiElement} from "./Base/VariableUIElement";
-import {UIEventSource} from "../Logic/UIEventSource";
+import {UIEventSource} from "../../Logic/UIEventSource";
+import {UIElement} from "../UIElement";
+import {VariableUiElement} from "../Base/VariableUIElement";
+import State from "../../State";
+import CheckBox from "../Input/CheckBox";
+import Combine from "../Base/Combine";
+import {FixedUiElement} from "../Base/FixedUiElement";
+import Translations from "../i18n/Translations";
-export class LayerSelection extends UIElement {
+export default class LayerSelection extends UIElement {
private readonly _checkboxes: UIElement[];
@@ -16,7 +16,9 @@ export class LayerSelection extends UIElement {
this._checkboxes = [];
for (const layer of State.state.filteredLayers.data) {
- const leafletStyle = layer.layerDef.GenerateLeafletStyle(new UIEventSource({id: "node/-1"}), true)
+ const leafletStyle = layer.layerDef.GenerateLeafletStyle(
+ new UIEventSource({id: "node/-1"}),
+ false)
const leafletHtml = leafletStyle.icon.html;
const icon =
new FixedUiElement(leafletHtml.Render())
diff --git a/UI/MoreScreen.ts b/UI/BigComponents/MoreScreen.ts
similarity index 84%
rename from UI/MoreScreen.ts
rename to UI/BigComponents/MoreScreen.ts
index 6a49ddb..7fe81eb 100644
--- a/UI/MoreScreen.ts
+++ b/UI/BigComponents/MoreScreen.ts
@@ -1,17 +1,17 @@
-import {UIElement} from "./UIElement";
-import {VerticalCombine} from "./Base/VerticalCombine";
-import Translations from "./i18n/Translations";
-import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
-import Combine from "./Base/Combine";
-import {SubtleButton} from "./Base/SubtleButton";
-import State from "../State";
-import {VariableUiElement} from "./Base/VariableUIElement";
-import Svg from "../Svg";
-import LayoutConfig from "../Customizations/JSON/LayoutConfig";
-import * as personal from "../assets/themes/personalLayout/personalLayout.json"
-import Constants from "../Models/Constants";
+import {VerticalCombine} from "../Base/VerticalCombine";
+import {UIElement} from "../UIElement";
+import {VariableUiElement} from "../Base/VariableUIElement";
+import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
+import {AllKnownLayouts} from "../../Customizations/AllKnownLayouts";
+import Svg from "../../Svg";
+import State from "../../State";
+import Combine from "../Base/Combine";
+import {SubtleButton} from "../Base/SubtleButton";
+import Translations from "../i18n/Translations";
+import * as personal from "../../assets/themes/personalLayout/personalLayout.json"
+import Constants from "../../Models/Constants";
-export class MoreScreen extends UIElement {
+export default class MoreScreen extends UIElement {
constructor() {
diff --git a/UI/PersonalLayersPanel.ts b/UI/BigComponents/PersonalLayersPanel.ts
similarity index 86%
rename from UI/PersonalLayersPanel.ts
rename to UI/BigComponents/PersonalLayersPanel.ts
index 9cdac9a..f5d9c12 100644
--- a/UI/PersonalLayersPanel.ts
+++ b/UI/BigComponents/PersonalLayersPanel.ts
@@ -1,17 +1,16 @@
-import {UIElement} from "./UIElement";
-import State from "../State";
-import Translations from "../UI/i18n/Translations";
-import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
-import Combine from "../UI/Base/Combine";
-import CheckBox from "../UI/Input/CheckBox";
-import * as personal from "../assets/themes/personalLayout/personalLayout.json";
-import {SubtleButton} from "./Base/SubtleButton";
-import {FixedUiElement} from "./Base/FixedUiElement";
-import Svg from "../Svg";
-import LayoutConfig from "../Customizations/JSON/LayoutConfig";
-import {UIEventSource} from "../Logic/UIEventSource";
-
-export class PersonalLayersPanel extends UIElement {
+import {UIEventSource} from "../../Logic/UIEventSource";
+import {UIElement} from "../UIElement";
+import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
+import {AllKnownLayouts} from "../../Customizations/AllKnownLayouts";
+import Svg from "../../Svg";
+import State from "../../State";
+import Combine from "../Base/Combine";
+import CheckBox from "../Input/CheckBox";
+import {SubtleButton} from "../Base/SubtleButton";
+import {FixedUiElement} from "../Base/FixedUiElement";
+import Translations from "../i18n/Translations";
+import * as personal from "../../assets/themes/personalLayout/personalLayout.json"
+export default class PersonalLayersPanel extends UIElement {
private checkboxes: UIElement[] = [];
constructor() {
diff --git a/UI/SearchAndGo.ts b/UI/BigComponents/SearchAndGo.ts
similarity index 69%
rename from UI/SearchAndGo.ts
rename to UI/BigComponents/SearchAndGo.ts
index 437b95c..6603ef8 100644
--- a/UI/SearchAndGo.ts
+++ b/UI/BigComponents/SearchAndGo.ts
@@ -1,23 +1,22 @@
-import Locale from "./i18n/Locale";
-import {UIElement} from "./UIElement";
-import {VariableUiElement} from "./Base/VariableUIElement";
-import {TextField} from "./Input/TextField";
-import {Geocoding} from "../Logic/Osm/Geocoding";
-import Translations from "./i18n/Translations";
-import State from "../State";
+import Locale from "../i18n/Locale";
+import {UIEventSource} from "../../Logic/UIEventSource";
+import {UIElement} from "../UIElement";
+import {Translation} from "../i18n/Translation";
+import {VariableUiElement} from "../Base/VariableUIElement";
+import Svg from "../../Svg";
+import State from "../../State";
+import {TextField} from "../Input/TextField";
+import {Geocoding} from "../../Logic/Osm/Geocoding";
+import Translations from "../i18n/Translations";
-import {UIEventSource} from "../Logic/UIEventSource";
-import Svg from "../Svg";
-import {Translation} from "./i18n/Translation";
-
-export class SearchAndGo extends UIElement {
+export default class SearchAndGo extends UIElement {
private _placeholder = new UIEventSource(Translations.t.general.search.search)
private _searchField = new TextField({
placeholder: new VariableUiElement(
this._placeholder.map(uiElement => uiElement.InnerRender(), [Locale.language])
),
- value: new UIEventSource("")
+ value: new UIEventSource("")
}
);
@@ -39,25 +38,31 @@ export class SearchAndGo extends UIElement {
}
+ InnerRender(): string {
+ return this._searchField.Render() +
+ this._goButton.Render();
+
+ }
+
// Triggered by 'enter' or onclick
private RunSearch() {
const searchString = this._searchField.GetValue().data;
- if(searchString === undefined || searchString === ""){
+ if (searchString === undefined || searchString === "") {
return;
}
this._searchField.GetValue().setData("");
this._placeholder.setData(Translations.t.general.search.searching);
const self = this;
- Geocoding.Search(searchString, (result) => {
+ Geocoding.Search(searchString, (result) => {
- console.log("Search result", result)
+ console.log("Search result", result)
if (result.length == 0) {
self._placeholder.setData(Translations.t.general.search.nothing);
return;
}
const bb = result[0].boundingbox;
- const bounds : [[number, number], [number, number]] = [
+ const bounds: [[number, number], [number, number]] = [
[bb[0], bb[2]],
[bb[1], bb[3]]
]
@@ -71,11 +76,5 @@ export class SearchAndGo extends UIElement {
}
- InnerRender(): string {
- return this._searchField.Render() +
- this._goButton.Render();
-
- }
-
}
\ No newline at end of file
diff --git a/UI/ShareButton.ts b/UI/BigComponents/ShareButton.ts
similarity index 96%
rename from UI/ShareButton.ts
rename to UI/BigComponents/ShareButton.ts
index 9fadc94..b4c2eba 100644
--- a/UI/ShareButton.ts
+++ b/UI/BigComponents/ShareButton.ts
@@ -1,4 +1,4 @@
-import {UIElement} from "./UIElement";
+import {UIElement} from "../UIElement";
export default class ShareButton extends UIElement{
private _embedded: UIElement;
diff --git a/UI/ShareScreen.ts b/UI/BigComponents/ShareScreen.ts
similarity index 90%
rename from UI/ShareScreen.ts
rename to UI/BigComponents/ShareScreen.ts
index c30882f..3f61e43 100644
--- a/UI/ShareScreen.ts
+++ b/UI/BigComponents/ShareScreen.ts
@@ -1,21 +1,21 @@
-import {UIElement} from "./UIElement";
-import Translations from "./i18n/Translations";
-import {FixedUiElement} from "./Base/FixedUiElement";
-import Combine from "./Base/Combine";
-import {VariableUiElement} from "./Base/VariableUIElement";
-import CheckBox from "./Input/CheckBox";
-import {VerticalCombine} from "./Base/VerticalCombine";
-import State from "../State";
-import {FilteredLayer} from "../Logic/FilteredLayer";
-import {Utils} from "../Utils";
-import {UIEventSource} from "../Logic/UIEventSource";
-import {SubtleButton} from "./Base/SubtleButton";
-import Svg from "../Svg";
-import {Translation} from "./i18n/Translation";
-import LayoutConfig from "../Customizations/JSON/LayoutConfig";
-import Constants from "../Models/Constants";
+import {VerticalCombine} from "../Base/VerticalCombine";
+import {UIElement} from "../UIElement";
+import {VariableUiElement} from "../Base/VariableUIElement";
+import {Translation} from "../i18n/Translation";
+import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
+import Svg from "../../Svg";
+import Combine from "../Base/Combine";
+import {SubtleButton} from "../Base/SubtleButton";
+import {UIEventSource} from "../../Logic/UIEventSource";
+import {Utils} from "../../Utils";
+import State from "../../State";
+import CheckBox from "../Input/CheckBox";
+import {FixedUiElement} from "../Base/FixedUiElement";
+import Translations from "../i18n/Translations";
+import Constants from "../../Models/Constants";
+import LayerConfig from "../../Customizations/JSON/LayerConfig";
-export class ShareScreen extends UIElement {
+export default class ShareScreen extends UIElement {
private readonly _options: UIElement;
private readonly _iframeCode: UIElement;
public iframe: UIEventSource;
@@ -61,7 +61,7 @@ export class ShareScreen extends UIElement {
}, [currentLocation]));
- function fLayerToParam(flayer: FilteredLayer) {
+ function fLayerToParam(flayer: {isDisplayed: UIEventSource, layerDef: LayerConfig}) {
if (flayer.isDisplayed.data) {
return null; // Being displayed is the default
}
diff --git a/UI/SimpleAddUI.ts b/UI/BigComponents/SimpleAddUI.ts
similarity index 93%
rename from UI/SimpleAddUI.ts
rename to UI/BigComponents/SimpleAddUI.ts
index 70e4aaf..83309e3 100644
--- a/UI/SimpleAddUI.ts
+++ b/UI/BigComponents/SimpleAddUI.ts
@@ -1,20 +1,19 @@
-import {UIElement} from "./UIElement";
-import {Tag, TagUtils} from "../Logic/Tags";
-import Translations from "./i18n/Translations";
-import Combine from "./Base/Combine";
-import {SubtleButton} from "./Base/SubtleButton";
-import Locale from "./i18n/Locale";
-import State from "../State";
-
-import {UIEventSource} from "../Logic/UIEventSource";
-import Svg from "../Svg";
-import {FixedUiElement} from "./Base/FixedUiElement";
-import Constants from "../Models/Constants";
-
/**
* Asks to add a feature at the last clicked location, at least if zoom is sufficient
*/
-export class SimpleAddUI extends UIElement {
+import Locale from "../i18n/Locale";
+import {UIEventSource} from "../../Logic/UIEventSource";
+import {Tag, TagUtils} from "../../Logic/Tags";
+import {UIElement} from "../UIElement";
+import Svg from "../../Svg";
+import {SubtleButton} from "../Base/SubtleButton";
+import State from "../../State";
+import Combine from "../Base/Combine";
+import {FixedUiElement} from "../Base/FixedUiElement";
+import Translations from "../i18n/Translations";
+import Constants from "../../Models/Constants";
+
+export default class SimpleAddUI extends UIElement {
private readonly _addButtons: UIElement[];
private _loginButton : UIElement;
diff --git a/UI/UserBadge.ts b/UI/BigComponents/UserBadge.ts
similarity index 85%
rename from UI/UserBadge.ts
rename to UI/BigComponents/UserBadge.ts
index b4b34a2..a1ad4b5 100644
--- a/UI/UserBadge.ts
+++ b/UI/BigComponents/UserBadge.ts
@@ -1,25 +1,25 @@
-import {UIElement} from "./UIElement";
-import {FixedUiElement} from "./Base/FixedUiElement";
-import {VariableUiElement} from "./Base/VariableUIElement";
-import Translations from "./i18n/Translations";
-import {UserDetails} from "../Logic/Osm/OsmConnection";
-import State from "../State";
-import {UIEventSource} from "../Logic/UIEventSource";
-import Combine from "./Base/Combine";
-import Svg from "../Svg";
-import Link from "./Base/Link";
-import LanguagePicker from "./LanguagePicker";
-
/**
* Handles and updates the user badge
*/
-export class UserBadge extends UIElement {
+import {UIEventSource} from "../../Logic/UIEventSource";
+import {UIElement} from "../UIElement";
+import {VariableUiElement} from "../Base/VariableUIElement";
+import {UserDetails} from "../../Logic/Osm/OsmConnection";
+import Svg from "../../Svg";
+import State from "../../State";
+import Combine from "../Base/Combine";
+import {FixedUiElement} from "../Base/FixedUiElement";
+import LanguagePicker from "../LanguagePicker";
+import Translations from "../i18n/Translations";
+import Link from "../Base/Link";
+
+export default class UserBadge extends UIElement {
private _userDetails: UIEventSource;
private _logout: UIElement;
private _homeButton: UIElement;
private _languagePicker: UIElement;
- private _loginButton : UIElement;
+ private _loginButton: UIElement;
constructor() {
super(State.state.osmConnection.userDetails);
@@ -94,7 +94,7 @@ export class UserBadge extends UIElement {
const settings =
new Link(Svg.gear_svg(),
- `https://www.openstreetmap.org/user/${encodeURIComponent(user.name)}/account`,
+ `https://www.openstreetmap.org/user/${encodeURIComponent(user.name)}/account`,
true)
diff --git a/UI/WelcomeMessage.ts b/UI/BigComponents/WelcomeMessage.ts
similarity index 80%
rename from UI/WelcomeMessage.ts
rename to UI/BigComponents/WelcomeMessage.ts
index 2851236..de1179c 100644
--- a/UI/WelcomeMessage.ts
+++ b/UI/BigComponents/WelcomeMessage.ts
@@ -1,12 +1,12 @@
-import {UIElement} from "./UIElement";
-import Locale from "../UI/i18n/Locale";
-import State from "../State";
-import Translations from "./i18n/Translations";
-import Combine from "./Base/Combine";
-import LanguagePicker from "./LanguagePicker";
+import Locale from "../i18n/Locale";
+import {UIElement} from "../UIElement";
+import State from "../../State";
+import Combine from "../Base/Combine";
+import LanguagePicker from "../LanguagePicker";
+import Translations from "../i18n/Translations";
-export class WelcomeMessage extends UIElement {
+export default class WelcomeMessage extends UIElement {
private languagePicker: UIElement;
private readonly description: UIElement;
@@ -25,11 +25,6 @@ export class WelcomeMessage extends UIElement {
"", layout.title, "
",
layout.description
])
- layout.descriptionTail
-
-
-
-
this.plzLogIn =
Translations.t.general.loginWithOpenStreetMap
.onClick(() => {
diff --git a/UI/CenterMessageBox.ts b/UI/CenterMessageBox.ts
index 71e2fdf..66fe288 100644
--- a/UI/CenterMessageBox.ts
+++ b/UI/CenterMessageBox.ts
@@ -2,10 +2,9 @@ import {UIElement} from "./UIElement";
import Translations from "./i18n/Translations";
import State from "../State";
-export class CenterMessageBox extends UIElement {
+export default class CenterMessageBox extends UIElement {
- constructor(
- ) {
+ constructor() {
super(State.state.centerMessage);
this.ListenTo(State.state.locationControl);
@@ -19,14 +18,17 @@ export class CenterMessageBox extends UIElement {
return {innerHtml: State.state.centerMessage.data, done: false};
}
const lu = State.state.layerUpdater;
- if(lu.retries.data > 0) {
- return {innerHtml: Translations.t.centerMessage.retrying.Subs({count: ""+ lu.retries.data}).Render(), done: false};
+ if (lu.retries.data > 0) {
+ return {
+ innerHtml: Translations.t.centerMessage.retrying.Subs({count: "" + lu.retries.data}).Render(),
+ done: false
+ };
}
-
+
if (lu.runningQuery.data) {
return {innerHtml: Translations.t.centerMessage.loadingData.Render(), done: false};
-
- }
+
+ }
if (!lu.sufficientlyZoomed.data) {
return {innerHtml: Translations.t.centerMessage.zoomIn.Render(), done: false};
} else {
diff --git a/UI/FullScreenMessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts
index 2451495..4b19508 100644
--- a/UI/FullScreenMessageBoxHandler.ts
+++ b/UI/FullScreenMessageBoxHandler.ts
@@ -6,7 +6,7 @@ import Combine from "./Base/Combine";
/**
* Handles the full screen popup on mobile
*/
-export class FullScreenMessageBox extends UIElement {
+export default class FullScreenMessageBox extends UIElement {
private readonly returnToTheMap: UIElement;
private _content: UIElement;
diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts
index 0481eef..68d1452 100644
--- a/UI/Popup/EditableTagRendering.ts
+++ b/UI/Popup/EditableTagRendering.ts
@@ -9,8 +9,8 @@ import State from "../../State";
import Svg from "../../Svg";
export default class EditableTagRendering extends UIElement {
- private _tags: UIEventSource;
- private _configuration: TagRenderingConfig;
+ private readonly _tags: UIEventSource;
+ private readonly _configuration: TagRenderingConfig;
private _editMode: UIEventSource = new UIEventSource(false);
private _editButton: UIElement;
diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts
index c714e5a..7a58c93 100644
--- a/UI/Popup/FeatureInfoBox.ts
+++ b/UI/Popup/FeatureInfoBox.ts
@@ -8,14 +8,14 @@ import TagRenderingAnswer from "./TagRenderingAnswer";
import State from "../../State";
import {FixedUiElement} from "../Base/FixedUiElement";
-export class FeatureInfoBox extends UIElement {
+export default class FeatureInfoBox extends UIElement {
private _tags: UIEventSource;
private _layerConfig: LayerConfig;
- private _title : UIElement;
+ private _title: UIElement;
private _titleIcons: UIElement;
private _renderings: UIElement[];
- private _questionBox : UIElement;
+ private _questionBox: UIElement;
constructor(
tags: UIEventSource,
@@ -35,15 +35,15 @@ export class FeatureInfoBox extends UIElement {
this._titleIcons = new Combine(
layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon)))
.SetClass("featureinfobox-icons");
-
- let questionBox : UIElement = undefined;
+
+ let questionBox: UIElement = undefined;
if (State.state.featureSwitchUserbadge.data) {
questionBox = new QuestionBox(tags, layerConfig.tagRenderings);
}
-
+
let questionBoxIsUsed = false;
this._renderings = layerConfig.tagRenderings.map(tr => {
- if(tr.question === null){
+ if (tr.question === null) {
questionBoxIsUsed = true;
// This is the question box!
return questionBox;
@@ -51,9 +51,9 @@ export class FeatureInfoBox extends UIElement {
return new EditableTagRendering(tags, tr);
});
this._renderings[0]?.SetClass("first-rendering");
- if(!questionBoxIsUsed){
- this._renderings.push(questionBox);
- }
+ if (!questionBoxIsUsed) {
+ this._renderings.push(questionBox);
+ }
}
InnerRender(): string {
diff --git a/UI/Popup/QuestionBox.ts b/UI/Popup/QuestionBox.ts
index e6caecb..991d11a 100644
--- a/UI/Popup/QuestionBox.ts
+++ b/UI/Popup/QuestionBox.ts
@@ -9,9 +9,9 @@ import Translations from "../i18n/Translations";
* Generates all the questions, one by one
*/
export default class QuestionBox extends UIElement {
- private _tags: UIEventSource;
+ private readonly _tags: UIEventSource;
- private _tagRenderings: TagRenderingConfig[];
+ private readonly _tagRenderings: TagRenderingConfig[];
private _tagRenderingQuestions: UIElement[];
private _skippedQuestions: UIEventSource = new UIEventSource([])
diff --git a/UI/Popup/SaveButton.ts b/UI/Popup/SaveButton.ts
index 2886d9d..3b741a4 100644
--- a/UI/Popup/SaveButton.ts
+++ b/UI/Popup/SaveButton.ts
@@ -1,7 +1,7 @@
import {UIEventSource} from "../../Logic/UIEventSource";
import {UIElement} from "../UIElement";
import Translations from "../i18n/Translations";
-import {OsmConnection, UserDetails} from "../../Logic/Osm/OsmConnection";
+import UserDetails, {OsmConnection} from "../../Logic/Osm/OsmConnection";
export class SaveButton extends UIElement {
diff --git a/UI/Popup/TagRenderingAnswer.ts b/UI/Popup/TagRenderingAnswer.ts
index d5e062d..57ddf68 100644
--- a/UI/Popup/TagRenderingAnswer.ts
+++ b/UI/Popup/TagRenderingAnswer.ts
@@ -7,7 +7,7 @@ import {SubstitutedTranslation} from "../SpecialVisualizations";
* Displays the correct value for a known tagrendering
*/
export default class TagRenderingAnswer extends UIElement {
- private _tags: UIEventSource;
+ private readonly _tags: UIEventSource;
private _configuration: TagRenderingConfig;
private _content: UIElement;
diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts
index c6420f5..bfeb82e 100644
--- a/UI/Popup/TagRenderingQuestion.ts
+++ b/UI/Popup/TagRenderingQuestion.ts
@@ -25,7 +25,7 @@ import Constants from "../../Models/Constants";
* Note that the value _migh_ already be known, e.g. when selected or when changing the value
*/
export default class TagRenderingQuestion extends UIElement {
- private _tags: UIEventSource;
+ private readonly _tags: UIEventSource;
private _configuration: TagRenderingConfig;
private _saveButton: UIElement;
diff --git a/UI/ShowDataLayer.ts b/UI/ShowDataLayer.ts
new file mode 100644
index 0000000..18d2986
--- /dev/null
+++ b/UI/ShowDataLayer.ts
@@ -0,0 +1,141 @@
+/**
+ * The data layer shows all the given geojson elements with the appropriate icon etc
+ */
+import {UIEventSource} from "../Logic/UIEventSource";
+import * as L from "leaflet"
+import LayerConfig from "../Customizations/JSON/LayerConfig";
+import State from "../State";
+import LazyElement from "./Base/LazyElement";
+import Hash from "../Logic/Web/Hash";
+import {GeoOperations} from "../Logic/GeoOperations";
+import FeatureInfoBox from "./Popup/FeatureInfoBox";
+
+export default class ShowDataLayer {
+
+ private readonly _layerDict;
+ private readonly _leafletMap: UIEventSource;
+
+ constructor(features: UIEventSource<{ feature: any, freshness: Date }[]>,
+ leafletMap: UIEventSource,
+ layers: { layerDef: LayerConfig, isDisplayed: UIEventSource }[]) {
+ this._leafletMap = leafletMap;
+ const self = this;
+
+ let oldGeoLayer: L.Layer = undefined;
+
+ this._layerDict = {};
+ for (const layer of layers) {
+ this._layerDict[layer.layerDef.id] = layer.layerDef;
+ }
+
+ function update() {
+ if (features.data === undefined) {
+ return;
+ }
+ if (leafletMap.data === undefined) {
+ return;
+ }
+ const mp = leafletMap.data;
+
+ const feats = features.data.map(ff => ff.feature);
+ const geoLayer = self.CreateGeojsonLayer(feats);
+
+ if (oldGeoLayer) {
+ mp.removeLayer(oldGeoLayer);
+ }
+
+ geoLayer.addTo(mp);
+ oldGeoLayer = geoLayer;
+ }
+
+ features.addCallbackAndRun(() => update());
+ leafletMap.addCallback(() => update());
+
+ }
+
+
+ private createStyleFor(feature) {
+ const tagsSource = State.state.allElements.getEventSourceFor(feature);
+ // Every object is tied to exactly one layer
+ const layer = this._layerDict[feature._matching_layer_id];
+ return layer.GenerateLeafletStyle(tagsSource, layer._showOnPopup !== undefined);
+ }
+
+ private pointToLayer(feature, latLng): L.Layer {
+ // Leaflet cannot handle geojson points natively
+ // We have to convert them to the appropriate icon
+ // Click handling is done in the next step
+
+ const tagSource = State.state.allElements.getEventSourceFor(feature);
+ const layer : LayerConfig = this._layerDict[feature._matching_layer_id];
+
+ const style = layer.GenerateLeafletStyle(tagSource, !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0));
+ return L.marker(latLng, {
+ icon: L.divIcon({
+ html: style.icon.html.Render(),
+ className: style.icon.className,
+ iconAnchor: style.icon.iconAnchor,
+ iconUrl: style.icon.iconUrl,
+ popupAnchor: style.icon.popupAnchor,
+ iconSize: style.icon.iconSize
+ })
+ });
+ }
+
+ private postProcessFeature(feature, leafletLayer: L.Layer){
+ const layer : LayerConfig = this._layerDict[feature._matching_layer_id];
+ if (layer.title === undefined && (layer.tagRenderings ?? []).length === 0) {
+ // No popup action defined -> Don't do anything
+ return;
+ }
+
+ const popup = L.popup({
+ autoPan: true,
+ closeOnEscapeKey: true,
+ }, leafletLayer);
+
+
+ const tags = State.state.allElements.getEventSourceFor(feature);
+ let uiElement: LazyElement = new LazyElement(() => new FeatureInfoBox(tags, layer));
+ popup.setContent(uiElement.Render());
+ leafletLayer.bindPopup(popup);
+ // We first render the UIelement (which'll still need an update later on...)
+ // But at least it'll be visible already
+
+
+ leafletLayer.on("click", (e) => {
+ // We set the element as selected...
+ uiElement.Activate();
+ State.state.selectedElement.setData(feature);
+ });
+
+
+ if (feature.properties.id.replace(/\//g, "_") === Hash.Get().data) {
+ // This element is in the URL, so this is a share link
+ // We already open it
+ uiElement.Activate();
+ popup.setContent(uiElement.Render());
+
+ const center = GeoOperations.centerpoint(feature).geometry.coordinates;
+ popup.setLatLng({lat: center[1], lng: center[0]});
+ popup.openOn(State.state.leafletMap.data);
+ State.state.selectedElement.setData(feature);
+ uiElement.Update();
+ }
+ }
+
+ private CreateGeojsonLayer(features: any[]): L.Layer {
+ const self = this;
+ const data = {
+ type: "FeatureCollection",
+ features: features
+ }
+ return L.geoJSON(data, {
+ style: feature => self.createStyleFor(feature),
+ pointToLayer: (feature, latLng) => self.pointToLayer(feature, latLng),
+ onEachFeature: (feature, leafletLayer) => self.postProcessFeature(feature, leafletLayer)
+ });
+
+ }
+
+}
\ No newline at end of file
diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts
index d973a85..8814242 100644
--- a/UI/SpecialVisualizations.ts
+++ b/UI/SpecialVisualizations.ts
@@ -9,7 +9,7 @@ import Locale from "../UI/i18n/Locale";
import {ImageUploadFlow} from "./Image/ImageUploadFlow";
import {Translation} from "./i18n/Translation";
-import ShareButton from "./ShareButton";
+import ShareButton from "./BigComponents/ShareButton";
import Svg from "../Svg";
import ReviewElement from "./Reviews/ReviewElement";
import MangroveReviews from "../Logic/Web/MangroveReviews";
diff --git a/assets/layers/direction/direction.json b/assets/layers/direction/direction.json
index f153319..a22f428 100644
--- a/assets/layers/direction/direction.json
+++ b/assets/layers/direction/direction.json
@@ -5,7 +5,10 @@
},
"minzoom": 16,
"overpassTags": {
- "or": ["camera:direction~*","direction~*"]
+ "or": [
+ "camera:direction~*",
+ "direction~*"
+ ]
},
"doNotDownload": true,
"passAllFeatures": true,
@@ -14,7 +17,16 @@
"en": "This layer visualizes directions"
},
"tagRenderings": [],
- "icon": "./assets/svg/direction_gradient.svg",
+ "icon": {
+ "render": "direction_gradient:var(--catch-detail-color)",
+ "#": "For some weird reason, showing the icon in the layer control panel breaks the svg-gradient (because the svg gradient has a global color or smthng) - so we use a different icon without gradient",
+ "mappings": [
+ {
+ "if": "id=node/-1",
+ "then": "direction:var(--catch-detail-color)"
+ }
+ ]
+ },
"rotation": {
"render": "{camera:direction}deg",
"mappings": [
@@ -24,7 +36,7 @@
}
]
},
- "iconSize": "200,200,center",
+ "iconSize": "200,200,center",
"color": "--catch-detail-color",
"stroke": "0",
"presets": [],
diff --git a/assets/layers/surveillance_cameras/surveillance_cameras.json b/assets/layers/surveillance_cameras/surveillance_cameras.json
index 5ae0be7..32c6847 100644
--- a/assets/layers/surveillance_cameras/surveillance_cameras.json
+++ b/assets/layers/surveillance_cameras/surveillance_cameras.json
@@ -335,6 +335,7 @@
]
},
"rotation": {
+ "#": "Note: {camera:direction} is substituted by a number, giving the string 'calc(123deg + 90deg)' ; it is this string that is used as css property, which interprets the calc",
"render": "calc({camera:direction}deg + 90deg)",
"mappings": [
{
diff --git a/assets/svg/direction.svg b/assets/svg/direction.svg
index e1eccac..cff71b6 100644
--- a/assets/svg/direction.svg
+++ b/assets/svg/direction.svg
@@ -5,11 +5,11 @@
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
- id="svg8"
- version="1.1"
- viewBox="0 0 100 100"
+ width="100"
height="100"
- width="100">
+ viewBox="0 0 100 100"
+ version="1.1"
+ id="svg8">
@@ -25,7 +25,7 @@
+ id="path821" />
diff --git a/assets/svg/direction_gradient.svg b/assets/svg/direction_gradient.svg
index 8ba4912..54af33b 100644
--- a/assets/svg/direction_gradient.svg
+++ b/assets/svg/direction_gradient.svg
@@ -6,48 +6,11 @@
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="100"
- height="100"
- viewBox="0 0 100 100"
- version="1.1"
id="svg8"
- sodipodi:docname="direction_gradient.svg"
- inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
-
-
-
-
+ version="1.1"
+ viewBox="0 0 100 100"
+ height="100"
+ width="100">
@@ -56,7 +19,6 @@
image/svg+xml
-
@@ -65,28 +27,27 @@
+ id="innercolor" />
+ id="outercolor" />
+ cx="49.787739"
+ cy="53.828533"
+ fx="49.787739"
+ fy="53.828533"
+ r="28.883806"
+ gradientTransform="matrix(1.5439431,-0.01852438,0.02075364,1.7297431,-27.986574,-42.187244)"
+ gradientUnits="userSpaceOnUse" />
+ d="M 50,50 21.042889,9.3993342 C 36.191421,-2.001434 60.726552,-3.6768009 78.8105,9.1490935 Z"
+ style="fill:url(#radialGradient828);fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />