More refactoring, stabilizing rotation and direction_gradient

This commit is contained in:
Pieter Vander Vennet 2021-01-04 04:06:21 +01:00
parent 5fec108ba2
commit 778044d0fb
45 changed files with 656 additions and 640 deletions

View file

@ -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<boolean>;
constructor(upstream :UIElement,
swtch: UIEventSource<boolean>) {
super(swtch);
this._upstream = upstream;
this._swtch = swtch;
}
InnerRender(): string {
if(this._swtch.data){
return this._upstream.Render();
}
return "";
}
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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<LayoutConfig>;
private readonly _userDetails: UIEventSource<UserDetails>;
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: `<img src='${layoutToUse.icon}'>`, 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, "<br/>Version " + Constants.vNumber]).Render();
}, [Locale.language]))
}
);
this._component = new TabbedComponent(tabs, State.state.welcomeMessageOpenedTab)
.ListenTo(this._userDetails);
}
InnerRender(): string {
return this._component.Render();
}
}

View file

@ -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, "<br/>", layerControlPanel]);
}
this._panel = layerControlPanel;
}
InnerRender(): string {
return this._panel.Render();
}
}

View file

@ -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<any>({id: "node/-1"}), true)
const leafletStyle = layer.layerDef.GenerateLeafletStyle(
new UIEventSource<any>({id: "node/-1"}),
false)
const leafletHtml = leafletStyle.icon.html;
const icon =
new FixedUiElement(leafletHtml.Render())

View file

@ -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() {

View file

@ -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() {

View file

@ -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<Translation>(Translations.t.general.search.search)
private _searchField = new TextField({
placeholder: new VariableUiElement(
this._placeholder.map(uiElement => uiElement.InnerRender(), [Locale.language])
),
value: new UIEventSource<string>("")
value: new UIEventSource<string>("")
}
);
@ -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();
}
}

View file

@ -1,4 +1,4 @@
import {UIElement} from "./UIElement";
import {UIElement} from "../UIElement";
export default class ShareButton extends UIElement{
private _embedded: UIElement;

View file

@ -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<string>;
@ -61,7 +61,7 @@ export class ShareScreen extends UIElement {
}, [currentLocation]));
function fLayerToParam(flayer: FilteredLayer) {
function fLayerToParam(flayer: {isDisplayed: UIEventSource<boolean>, layerDef: LayerConfig}) {
if (flayer.isDisplayed.data) {
return null; // Being displayed is the default
}

View file

@ -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;

View file

@ -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<UserDetails>;
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)

View file

@ -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 {
"<h3>", layout.title, "</h3>",
layout.description
])
layout.descriptionTail
this.plzLogIn =
Translations.t.general.loginWithOpenStreetMap
.onClick(() => {

View file

@ -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 {

View file

@ -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;

View file

@ -9,8 +9,8 @@ import State from "../../State";
import Svg from "../../Svg";
export default class EditableTagRendering extends UIElement {
private _tags: UIEventSource<any>;
private _configuration: TagRenderingConfig;
private readonly _tags: UIEventSource<any>;
private readonly _configuration: TagRenderingConfig;
private _editMode: UIEventSource<boolean> = new UIEventSource<boolean>(false);
private _editButton: UIElement;

View file

@ -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<any>;
private _layerConfig: LayerConfig;
private _title : UIElement;
private _title: UIElement;
private _titleIcons: UIElement;
private _renderings: UIElement[];
private _questionBox : UIElement;
private _questionBox: UIElement;
constructor(
tags: UIEventSource<any>,
@ -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 {

View file

@ -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<any>;
private readonly _tags: UIEventSource<any>;
private _tagRenderings: TagRenderingConfig[];
private readonly _tagRenderings: TagRenderingConfig[];
private _tagRenderingQuestions: UIElement[];
private _skippedQuestions: UIEventSource<number[]> = new UIEventSource<number[]>([])

View file

@ -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 {

View file

@ -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<any>;
private readonly _tags: UIEventSource<any>;
private _configuration: TagRenderingConfig;
private _content: UIElement;

View file

@ -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<any>;
private readonly _tags: UIEventSource<any>;
private _configuration: TagRenderingConfig;
private _saveButton: UIElement;

141
UI/ShowDataLayer.ts Normal file
View file

@ -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<L.Map>;
constructor(features: UIEventSource<{ feature: any, freshness: Date }[]>,
leafletMap: UIEventSource<L.Map>,
layers: { layerDef: LayerConfig, isDisplayed: UIEventSource<boolean> }[]) {
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)
});
}
}

View file

@ -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";