From 5b59d7dbd07102da810f42f10f496b6bc3b9650b Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 12 Nov 2020 12:18:02 +0100 Subject: [PATCH 01/20] Fixed small bugs, add documentation to query parameters, draft of surveillance cams --- Logic/Web/QueryParameters.ts | 2 +- State.ts | 11 +- UI/Input/MultiInput.ts | 8 +- UI/Popup/FeatureInfoBox.ts | 3 +- assets/layers/surveillance_cameras.json | 228 ++++++++++++++++++++++++ index.css | 5 + 6 files changed, 246 insertions(+), 11 deletions(-) create mode 100644 assets/layers/surveillance_cameras.json diff --git a/Logic/Web/QueryParameters.ts b/Logic/Web/QueryParameters.ts index 21a57cc..235ff7b 100644 --- a/Logic/Web/QueryParameters.ts +++ b/Logic/Web/QueryParameters.ts @@ -59,7 +59,7 @@ export class QueryParameters { } - public static GetQueryParameter(key: string, deflt: string): UIEventSource { + public static GetQueryParameter(key: string, deflt: string, documentation?: string): UIEventSource { if(!this.initialized){ this.init(); } diff --git a/State.ts b/State.ts index 19e041a..5843018 100644 --- a/State.ts +++ b/State.ts @@ -165,7 +165,7 @@ export default class State { }); - function featSw(key: string, deflt: (layout: LayoutConfig) => boolean): UIEventSource { + function featSw(key: string, deflt: (layout: LayoutConfig) => boolean, documentation?: string): UIEventSource { const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined); // I'm so sorry about someone trying to decipher this @@ -173,13 +173,14 @@ export default class State { return UIEventSource.flatten( self.layoutToUse.map((layout) => { const defaultValue = deflt(layout); - const queryParam = QueryParameters.GetQueryParameter(key, "" + defaultValue) + const queryParam = QueryParameters.GetQueryParameter(key, "" + defaultValue, documentation) return queryParam.map((str) => str === undefined ? defaultValue : (str !== "false")); }), [queryParameterSource]); } - this.featureSwitchUserbadge = featSw("fs-userbadge", (layoutToUse) => layoutToUse?.enableUserBadge ?? true); + this.featureSwitchUserbadge = featSw("fs-userbadge", (layoutToUse) => layoutToUse?.enableUserBadge ?? true, + "Disables the userbadge (and thus disables login capabilities)"); this.featureSwitchSearch = featSw("fs-search", (layoutToUse) => layoutToUse?.enableSearch ?? true); this.featureSwitchLayers = featSw("fs-layers", (layoutToUse) => layoutToUse?.enableLayers ?? true); this.featureSwitchAddNew = featSw("fs-add-new", (layoutToUse) => layoutToUse?.enableAddNewPoints ?? true); @@ -213,8 +214,10 @@ export default class State { continue; } try { + const json = btoa(customLayout.data); + console.log(json); const layout = new LayoutConfig( - JSON.parse(btoa(customLayout.data))); + JSON.parse(json)); installedThemes.push({ layout: layout, definition: customLayout.data diff --git a/UI/Input/MultiInput.ts b/UI/Input/MultiInput.ts index 22f9fdf..3e40480 100644 --- a/UI/Input/MultiInput.ts +++ b/UI/Input/MultiInput.ts @@ -71,7 +71,7 @@ export class MultiInput extends InputElement { input.IsSelected.addCallback(() => this.UpdateIsSelected()); const moveUpBtn = Svg.up_ui() - .onClick(() => { + .SetClass('small-image').onClick(() => { const v = self._value.data[i]; self._value.data[i] = self._value.data[i - 1]; self._value.data[i - 1] = v; @@ -79,8 +79,8 @@ export class MultiInput extends InputElement { }); const moveDownBtn = - Svg.down_ui().SetStyle('max-width: 1.5em; margin-left: 5px;display:block;') - .onClick(() => { + Svg.down_ui() + .SetClass('small-image') .onClick(() => { const v = self._value.data[i]; self._value.data[i] = self._value.data[i + 1]; self._value.data[i + 1] = v; @@ -98,7 +98,7 @@ export class MultiInput extends InputElement { const deleteBtn = - Svg.delete_icon_ui().SetStyle('max-width: 1.5em;width:1.5em; margin-left: 5px;') + Svg.delete_icon_ui().SetClass('small-image') .onClick(() => { self._value.data.splice(i, 1); self._value.ping(); diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index c0439c2..ddf23a3 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -5,6 +5,7 @@ import EditableTagRendering from "./EditableTagRendering"; import QuestionBox from "./QuestionBox"; import Combine from "../Base/Combine"; import TagRenderingAnswer from "./TagRenderingAnswer"; +import State from "../../State"; export class FeatureInfoBox extends UIElement { private _tags: UIEventSource; @@ -33,8 +34,6 @@ export class FeatureInfoBox extends UIElement { layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon))) .SetClass("featureinfobox-icons"); this._renderings = layerConfig.tagRenderings.map(tr => new EditableTagRendering(tags, tr)); - this._questionBox = new QuestionBox(tags, layerConfig.tagRenderings); - } InnerRender(): string { diff --git a/assets/layers/surveillance_cameras.json b/assets/layers/surveillance_cameras.json new file mode 100644 index 0000000..46126a5 --- /dev/null +++ b/assets/layers/surveillance_cameras.json @@ -0,0 +1,228 @@ +{ + "id": "surveillance", + "title": { + "en": "Surveillance under Surveillance", + "nl": "Surveillance under Surveillance" + }, + "shortDescription": { + "en": "Surveillance cameras and other means of surveillance", + "nl": "Bewakingscameras en dergelijke" + }, + "description": { + "en": "On this open map, you can find surveillance cameras.", + "nl": "Op deze open kaart kan je bewakingscamera's vinden." + }, + "language": [ + "en", + "nl" + ], + "maintainer": "", + "icon": "https://upload.wikimedia.org/wikipedia/commons/b/b7/Video_surveillance_logo.svg", + "version": "0", + "startLat": 0, + "startLon": 0, + "startZoom": 1, + "widenFactor": 0.05, + "socialImage": "", + "layers": [ + { + "id": "cameras", + "name": { + "en": "Surveillance camera's", + "nl": "Bewakingscamera's" + }, + "minzoom": 12, + "overpassTags": { + "and": [ + "man_made=surveillance", + { + "or": [ + "surveillance:type=camera", + "surveillance:type=ALPR", + "surveillance:type=ANPR" + ] + } + ] + }, + "title": { + "render": { + "en": "Surveillance Camera", + "nl": "Bewakingscamera" + } + }, + "description": {}, + "tagRenderings": [ + { + "question": { + "en": "What kind of camera is this?", + "nl": "Wat voor soort camera is dit?" + }, + "mappings": [ + { + "if": { + "and": [ + "camera:type=fixed" + ] + }, + "then": { + "en": "A fixed (non-moving) camera", + "nl": "Een vaste camera" + } + }, + { + "if": { + "and": [ + "camera:type=dome" + ] + }, + "then": { + "en": "A dome camera (which can turn)", + "nl": "Een dome (bolvormige camera die kan draaien)" + } + }, + { + "if": { + "and": [ + "camera:type=panning" + ] + }, + "then": { + "en": "A panning camera", + "nl": "Een camera die (met een motor) van links naar rechts kan draaien" + } + } + ] + }, + { + "freeform": { + "key": "operator" + }, + "question": { + "en": "Who operates this CCTV?", + "nl": "Wie beheert deze bewakingscamera?" + }, + "render": { + "en": "Operated by {operator}", + "nl": "Beheer door {operator}" + } + }, + { + "question": { + "en": "What k ind of surveillance is this camera", + "nl": "Wat soort bewaking wordt hier uitgevoerd?" + }, + "mappings": [ + { + "if": { + "and": [ + "surveillance=public" + ] + }, + "then": { + "en": "A public area is surveilled, such as a street, a bridge, a square, a park, a train station...", + "nl": "Bewaking van de publieke ruilmte, dus een straat, een brug, een park, een plein, een stationsgebouw..." + } + }, + { + "if": { + "and": [ + "surveillance=outdoor" + ] + }, + "then": { + "en": "An outdoor, yet private area is surveilled (e.g. a parking lot, a fuel station, courtyard, entrance, private driveway, ...)", + "nl": "Een buitenruimte met privaat karakter (zoals een privé-oprit, een parking, tankstation, ...)" + } + }, + { + "if": { + "and": [ + "surveillance=indoor" + ] + }, + "then": { + "nl": "Een private binnenruimte wordt bewaakt, bv. een wiinkel, een parkeergarage, ...", + "en": "A private indoor area is surveilled, e.g. a shop, a private underground parking, ..." + } + } + ] + }, + { + "question": { + "en": "What exactly is surveilled here?", + "nl": "Wat wordt hier precies bewaakt?" + }, + "freeform": { + "key": "surveillance:type" + }, + "render": { + "en": " Surveills a {surveillance:type}", + "nl": "Bewaakt een {surveillance:type}" + }, + "mappings": [ + { + "if": { + "and": [ + "surveillance:zone=parking" + ] + }, + "then": { + "en": "Surveills a parking", + "nl": "Bewaakt een parking" + } + }, + { + "if": { + "and": [ + "surveillance:zone=traffic" + ] + }, + "then": { + "en": "Surveills the traffic", + "nl": "Bewaakt het verkeer" + } + }, + { + "if": { + "and": [ + "surveillance:zone=entrance" + ] + }, + "then": { + "en": "Surveills an entrance", + "nl": "Bewaakt een ingang" + } + }, + { + "if": { + "and": [ + "surveillance:zone=shop" + ] + }, + "then": { + "en": "Surveills a shop", + "nl": "Bewaakt een winkel" + } + } + ] + } + ], + "hideUnderlayingFeaturesMinPercentage": 0, + "icon": { + "render": "https://upload.wikimedia.org/wikipedia/commons/b/b7/Video_surveillance_logo.svg" + }, + "width": { + "render": "8" + }, + "iconSize": { + "render": "30,30,center" + }, + "color": { + "render": "#00f" + }, + "presets": [], + "wayHandling": 2 + } + ], + "roamingRenderings": [] +} \ No newline at end of file diff --git a/index.css b/index.css index 135578a..8df3e9a 100644 --- a/index.css +++ b/index.css @@ -416,3 +416,8 @@ body { } +.small-image img { + height: 1em; + max-width: 1em; +} + From ba44024dd91baec2ddfdc4574026bc0ee92247b9 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Fri, 13 Nov 2020 23:58:11 +0100 Subject: [PATCH 02/20] Fixes, surveillance cams v0.1 --- Customizations/AllKnownLayouts.ts | 6 ++- InitUiElements.ts | 4 +- Logic/Web/QueryParameters.ts | 23 +++++++--- README.md | 46 ++++++++++++++++++- State.ts | 46 +++++++++++-------- UI/Image/DeleteImage.ts | 3 ++ UI/Image/ImageUploadFlow.ts | 4 ++ UI/Popup/TagRenderingAnswer.ts | 24 ++++------ .../surveillance_cameras.json | 26 ++++++++++- index.ts | 20 +------- 10 files changed, 137 insertions(+), 65 deletions(-) rename assets/{layers => themes/surveillance_cameras}/surveillance_cameras.json (89%) diff --git a/Customizations/AllKnownLayouts.ts b/Customizations/AllKnownLayouts.ts index e00ca6c..5c1c0a0 100644 --- a/Customizations/AllKnownLayouts.ts +++ b/Customizations/AllKnownLayouts.ts @@ -15,10 +15,11 @@ import * as benches from "../assets/themes/benches/benches.json"; import * as charging_stations from "../assets/themes/charging_stations/charging_stations.json" import * as widths from "../assets/themes/widths/width.json" import * as drinking_water from "../assets/themes/drinking_water/drinking_water.json" -import LayerConfig from "./JSON/LayerConfig"; -import SharedLayers from "./SharedLayers"; +import * as surveillance_cameras from "../assets/themes/surveillance_cameras/surveillance_cameras.json" import * as personal from "../assets/themes/personalLayout/personalLayout.json" +import LayerConfig from "./JSON/LayerConfig"; import LayoutConfig from "./JSON/LayoutConfig"; +import SharedLayers from "./SharedLayers"; export class AllKnownLayouts { @@ -60,6 +61,7 @@ export class AllKnownLayouts { new LayoutConfig(widths), new LayoutConfig(buurtnatuur), new LayoutConfig(bike_monitoring_stations), + new LayoutConfig(surveillance_cameras) ]; diff --git a/InitUiElements.ts b/InitUiElements.ts index 4b9a855..8310b0c 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -442,7 +442,7 @@ export class InitUiElements { State.state.layerUpdater = new UpdateFromOverpass(State.state); State.state.availableBackgroundLayers = new AvailableBaseLayers(State.state).availableEditorLayers; - const queryParam = QueryParameters.GetQueryParameter("background", State.state.layoutToUse.data.defaultBackgroundId); + const queryParam = QueryParameters.GetQueryParameter("background", State.state.layoutToUse.data.defaultBackgroundId, "The id of the background layer to start with"); queryParam.addCallbackAndRun((selectedId: string) => { const available = State.state.availableBackgroundLayers.data; @@ -483,7 +483,7 @@ export class InitUiElements { const flayer: FilteredLayer = FilteredLayer.fromDefinition(layer, generateInfo); flayers.push(flayer); - QueryParameters.GetQueryParameter("layer-" + layer.id, "true") + QueryParameters.GetQueryParameter("layer-" + layer.id, "true", "Wehter or not layer "+layer.id+" is shown") .map((str) => str !== "false", [], (b) => b.toString()) .syncWith( flayer.isDisplayed diff --git a/Logic/Web/QueryParameters.ts b/Logic/Web/QueryParameters.ts index 235ff7b..821c163 100644 --- a/Logic/Web/QueryParameters.ts +++ b/Logic/Web/QueryParameters.ts @@ -5,20 +5,22 @@ import {UIEventSource} from "../UIEventSource"; export class QueryParameters { - private static order: string [] = ["layout","test","z","lat","lon"]; + private static order: string [] = ["layout", "test", "z", "lat", "lon"]; private static knownSources = {}; private static initialized = false; private static defaults = {} - - private static addOrder(key){ - if(this.order.indexOf(key) < 0){ + + private static documentation = {} + + private static addOrder(key) { + if (this.order.indexOf(key) < 0) { this.order.push(key) } } private static init() { - - if(this.initialized){ + + if (this.initialized) { return; } this.initialized = true; @@ -63,6 +65,7 @@ export class QueryParameters { if(!this.initialized){ this.init(); } + QueryParameters.documentation[key] = documentation; if (deflt !== undefined) { QueryParameters.defaults[key] = deflt; } @@ -76,4 +79,12 @@ export class QueryParameters { return source; } + public static GenerateQueryParameterDocs(): string { + const docs = []; + for (const key in QueryParameters.documentation) { + docs.push("**" + key + "**: " + QueryParameters.documentation[key] + " (default value: _" + QueryParameters.defaults[key] + "_)") + } + return docs.join("\n\n"); + } + } \ No newline at end of file diff --git a/README.md b/README.md index 3f52712..1a2eed3 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ A theme has translations into the preset.json (`assets/themes/themename/themenam 1. Modify `"language"` to contain the new language, e.g. `"language": "nl"` becomes `"language": ["nl", "en"]` 2. Add extra strings to the texts. If it used to be a single-language theme, one can replace the strings, e.g.: `"description": "Welcome to Open Bookcase Map"` to `"description": {"en": "Welcome to Open Bookcase Map", "nl": "Welkom bij de OpenBoekenruilkastenKaart", "fr": "Bienvenue sûr la carte des petites bibliotheques"}`. If the correct language is not found, it'll fallback to another supported language. -3. If you notice missing translations in the core of MapComplete, fork this project, open [the file containing all translations](https://github.com/pietervdvn/MapComplete/blob/master/UI/i18n/Translations.ts), add add a language string there +3. If you notice missing translations in the core of MapComplete, fork this project, open [the file containing all translations](https://github.com/pietervdvn/MapComplete/blob/master/assets/translations.json), add add a language string there 4. Send a pull request to update the languages, I'll gladly add it! It doesn't have to be a complete translation from the start ;) ### Adding your theme to the repository @@ -165,6 +165,50 @@ Whenever a change is made -even adding a single tag- the change is uploaded into Note that changesets are closed automatically after one hour of inactivity, so we don't have to worry about closing them. +### Query parameters + +By adding extra query parameters, more options are available to influence: + +**test**: If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org (default value: _false_) + +**layout**: The layout to load into MapComplete (default value: _bookcases_) + +**userlayout**: undefined (default value: _false_) + +**layer-control-toggle**: Wether or not the layer control is shown (default value: _false_) + +**tab**: 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 >200 changesets) (default value: _0_) + +**z**: The initial/current zoom level (default value: _1_) + +**lat**: The initial/current latitude (default value: _0_) + +**lon**: The initial/current longitude of the app (default value: _0_) + +**fs-userbadge**: Disables/Enables the userbadge (and thus disables login capabilities) (default value: _true_) + +**fs-search**: Disables/Enables the search bar (default value: _true_) + +**fs-layers**: Disables/Enables the layer control (default value: _true_) + +**fs-add-new**: Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place) (default value: _true_) + +**fs-welcome-message**: undefined (default value: _true_) + +**fs-iframe**: Disables/Enables the iframe-popup (default value: _false_) + +**fs-more-quests**: Disables/Enables the 'More Quests'-tab in the welcome message (default value: _true_) + +**fs-share-screen**: Disables/Enables the 'Share-screen'-tab in the welcome message (default value: _true_) + +**fs-geolocation**: Disables/Enables the geolocation button (default value: _true_) + +**oauth_token**: Used to complete the login (default value: _undefined_) + +**background**: The id of the background layer to start with (default value: _undefined_) + +**layer-bookcases**: Wehter or not layer bookcases is shown (default value: _true_) index.ts:104:8 + # Privacy Privacy is important, we try to leak as little information as possible. diff --git a/State.ts b/State.ts index 0bbb779..5d7b63f 100644 --- a/State.ts +++ b/State.ts @@ -115,10 +115,10 @@ export default class State { public layoutDefinition: string; public installedThemes: UIEventSource<{ layout: LayoutConfig; definition: string }[]>; - public layerControlIsOpened: UIEventSource = QueryParameters.GetQueryParameter("layer-control-toggle", "false") + public layerControlIsOpened: UIEventSource = QueryParameters.GetQueryParameter("layer-control-toggle", "false", "Wether or not the layer control is shown") .map((str) => str !== "false", [], b => "" + b) - public welcomeMessageOpenedTab = QueryParameters.GetQueryParameter("tab", "0").map( + 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 >${State.userJourney.mapCompleteHelpUnlock} changesets)`).map( str => isNaN(Number(str)) ? 0 : Number(str), [], n => "" + n ); @@ -138,11 +138,11 @@ export default class State { }) } this.zoom = asFloat( - QueryParameters.GetQueryParameter("z", "" + layoutToUse.startZoom) + QueryParameters.GetQueryParameter("z", "" + layoutToUse.startZoom, "The initial/current zoom level") .syncWith(LocalStorageSource.Get("zoom"))); - this.lat = asFloat(QueryParameters.GetQueryParameter("lat", "" + layoutToUse.startLat) + this.lat = asFloat(QueryParameters.GetQueryParameter("lat", "" + layoutToUse.startLat, "The initial/current latitude") .syncWith(LocalStorageSource.Get("lat"))); - this.lon = asFloat(QueryParameters.GetQueryParameter("lon", "" + layoutToUse.startLon) + this.lon = asFloat(QueryParameters.GetQueryParameter("lon", "" + layoutToUse.startLon, "The initial/current longitude of the app") .syncWith(LocalStorageSource.Get("lon"))); @@ -165,8 +165,8 @@ export default class State { }); - function featSw(key: string, deflt: (layout: LayoutConfig) => boolean, documentation?: string): UIEventSource { - const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined); + 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 // It takes the current layout, extracts the default value for this query paramter. A query parameter event source is then retreived and flattened @@ -180,20 +180,30 @@ export default class State { this.featureSwitchUserbadge = featSw("fs-userbadge", (layoutToUse) => layoutToUse?.enableUserBadge ?? true, - "Disables the userbadge (and thus disables login capabilities)"); - this.featureSwitchSearch = featSw("fs-search", (layoutToUse) => layoutToUse?.enableSearch ?? true); - this.featureSwitchLayers = featSw("fs-layers", (layoutToUse) => layoutToUse?.enableLayers ?? true); - this.featureSwitchAddNew = featSw("fs-add-new", (layoutToUse) => layoutToUse?.enableAddNewPoints ?? true); - this.featureSwitchWelcomeMessage = featSw("fs-welcome-message", () => true); - this.featureSwitchIframe = featSw("fs-iframe", () => false); - this.featureSwitchMoreQuests = featSw("fs-more-quests", (layoutToUse) => layoutToUse?.enableMoreQuests ?? true); - this.featureSwitchShareScreen = featSw("fs-share-screen", (layoutToUse) => layoutToUse?.enableShareScreen ?? true); - this.featureSwitchGeolocation = featSw("fs-geolocation", (layoutToUse) => layoutToUse?.enableGeolocation ?? true); + "Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus disables editing all together, effectively putting MapComplete into read-only mode."); + this.featureSwitchSearch = featSw("fs-search", (layoutToUse) => layoutToUse?.enableSearch ?? true, + "Disables/Enables the search bar"); + this.featureSwitchLayers = featSw("fs-layers", (layoutToUse) => layoutToUse?.enableLayers ?? true, + "Disables/Enables the layer control"); + this.featureSwitchAddNew = featSw("fs-add-new", (layoutToUse) => layoutToUse?.enableAddNewPoints ?? true, + "Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place)"); + this.featureSwitchWelcomeMessage = featSw("fs-welcome-message", () => true, + "Disables/enables the help menu or welcome message"); + this.featureSwitchIframe = featSw("fs-iframe", () => false, + "Disables/Enables the iframe-popup"); + this.featureSwitchMoreQuests = featSw("fs-more-quests", (layoutToUse) => layoutToUse?.enableMoreQuests ?? true, + "Disables/Enables the 'More Quests'-tab in the welcome message"); + this.featureSwitchShareScreen = featSw("fs-share-screen", (layoutToUse) => layoutToUse?.enableShareScreen ?? true, + "Disables/Enables the 'Share-screen'-tab in the welcome message"); + this.featureSwitchGeolocation = featSw("fs-geolocation", (layoutToUse) => layoutToUse?.enableGeolocation ?? true, + "Disables/Enables the geolocation button"); - const testParam = QueryParameters.GetQueryParameter("test", "false").data; + const testParam = QueryParameters.GetQueryParameter("test", "false", + "If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org").data; this.osmConnection = new OsmConnection( testParam === "true", - QueryParameters.GetQueryParameter("oauth_token", undefined), + QueryParameters.GetQueryParameter("oauth_token", undefined, + "Used to complete the login"), layoutToUse.id, true ); diff --git a/UI/Image/DeleteImage.ts b/UI/Image/DeleteImage.ts index b59af06..7e5a2c1 100644 --- a/UI/Image/DeleteImage.ts +++ b/UI/Image/DeleteImage.ts @@ -42,6 +42,9 @@ export default class DeleteImage extends UIElement { } InnerRender(): string { + if(!State.state.featureSwitchUserbadge.data){ + return ""; + } const value = this.tags.data[this.key]; if (value === undefined || value === "") { diff --git a/UI/Image/ImageUploadFlow.ts b/UI/Image/ImageUploadFlow.ts index 0e8815a..2fb61bc 100644 --- a/UI/Image/ImageUploadFlow.ts +++ b/UI/Image/ImageUploadFlow.ts @@ -51,6 +51,10 @@ export class ImageUploadFlow extends UIElement { } InnerRender(): string { + + if(!State.state.featureSwitchUserbadge.data){ + return ""; + } const t = Translations.t.image; if (State.state.osmConnection.userDetails === undefined) { diff --git a/UI/Popup/TagRenderingAnswer.ts b/UI/Popup/TagRenderingAnswer.ts index 8c9f6dd..f560bf2 100644 --- a/UI/Popup/TagRenderingAnswer.ts +++ b/UI/Popup/TagRenderingAnswer.ts @@ -9,25 +9,11 @@ import {SubstitutedTranslation} from "../SpecialVisualizations"; export default class TagRenderingAnswer extends UIElement { private _tags: UIEventSource; private _configuration: TagRenderingConfig; - private _content: UIElement; constructor(tags: UIEventSource, configuration: TagRenderingConfig) { super(tags); this._tags = tags; this._configuration = configuration; - const self = this; - tags.addCallbackAndRun(tags => { - if (tags === undefined) { - self._content = undefined - return; - } - const tr = this._configuration.GetRenderValue(tags); - if (tr === undefined) { - self._content = undefined - return - } - self._content = new SubstitutedTranslation(tr, self._tags) - }) } InnerRender(): string { @@ -36,10 +22,16 @@ export default class TagRenderingAnswer extends UIElement { return ""; } } - if(this._content === undefined){ + + const tags = this._tags.data; + if (tags === undefined) { return ""; } - return this._content.Render(); + const tr = this._configuration.GetRenderValue(tags); + if (tr === undefined) { + return ""; + } + return new SubstitutedTranslation(tr, this._tags).Render(); } } \ No newline at end of file diff --git a/assets/layers/surveillance_cameras.json b/assets/themes/surveillance_cameras/surveillance_cameras.json similarity index 89% rename from assets/layers/surveillance_cameras.json rename to assets/themes/surveillance_cameras/surveillance_cameras.json index 46126a5..c2af520 100644 --- a/assets/layers/surveillance_cameras.json +++ b/assets/themes/surveillance_cameras/surveillance_cameras.json @@ -156,8 +156,8 @@ "key": "surveillance:type" }, "render": { - "en": " Surveills a {surveillance:type}", - "nl": "Bewaakt een {surveillance:type}" + "en": " Surveills a {surveillance:zone}", + "nl": "Bewaakt een {surveillance:zone}" }, "mappings": [ { @@ -193,6 +193,28 @@ "nl": "Bewaakt een ingang" } }, + { + "if": { + "and": [ + "surveillance:zone=corridor" + ] + }, + "then": { + "en": "Surveills a corridor", + "nl": "Bewaakt een gang" + } + }, + { + "if": { + "and": [ + "surveillance:zone=public_transport_platform" + ] + }, + "then": { + "en": "Surveills a public tranport platform", + "nl": "Bewaakt een perron of bushalte" + } + }, { "if": { "and": [ diff --git a/index.ts b/index.ts index 712f570..62b53f8 100644 --- a/index.ts +++ b/index.ts @@ -4,7 +4,6 @@ import {InitUiElements} from "./InitUiElements"; import {QueryParameters} from "./Logic/Web/QueryParameters"; import {UIEventSource} from "./Logic/UIEventSource"; import * as $ from "jquery"; -import SharedLayers from "./Customizations/SharedLayers"; import LayoutConfig from "./Customizations/JSON/LayoutConfig"; let defaultLayout = "bookcases" @@ -54,23 +53,7 @@ if (path !== "index.html" && path !== "") { defaultLayout = path.substr(0, path.length - 5); console.log("Using layout", defaultLayout); } - -// Run over all questsets. If a part of the URL matches a searched-for part in the layout, it'll take that as the default -for (const k in AllKnownLayouts.allSets) { - const layout : LayoutConfig= AllKnownLayouts.allSets[k]; - const possibleParts = (layout.locationContains ?? []); - for (const locationMatch of possibleParts) { - if (locationMatch === "") { - continue - } - if (window.location.href.toLowerCase().indexOf(locationMatch.toLowerCase()) >= 0) { - defaultLayout = layout.name; - } - } -} - -defaultLayout = QueryParameters.GetQueryParameter("layout", defaultLayout).data; - +defaultLayout = QueryParameters.GetQueryParameter("layout", defaultLayout,"The layout to load into MapComplete").data; let layoutToUse: LayoutConfig = AllKnownLayouts.allSets[defaultLayout.toLowerCase()] ?? AllKnownLayouts["all"]; @@ -118,3 +101,4 @@ if (layoutFromBase64.startsWith("wiki:")) { InitUiElements.InitAll(layoutToUse, layoutFromBase64, testing, defaultLayout); } +// console.log(QueryParameters.GenerateQueryParameterDocs()) From 3fdb84e481c222822ab8e957042fab418ca3c7f1 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 14 Nov 2020 02:54:33 +0100 Subject: [PATCH 03/20] Extract variables from html, add black theme to surveillance cameras, use svgs directly in the frontend --- InitUiElements.ts | 24 ++-- Svg.ts | 109 ++++++++++++------ UI/FullScreenMessageBoxHandler.ts | 3 +- UI/Image/ImageUploadFlow.ts | 6 +- UI/Input/Direction.ts | 39 +++++++ UI/Popup/EditableTagRendering.ts | 2 +- UI/UserBadge.ts | 12 +- assets/svg/ampersand.svg | 2 +- assets/svg/checkmark.svg | 2 +- assets/svg/envelope.svg | 51 +++++++- assets/svg/gear.svg | 5 +- assets/svg/help.svg | 22 +--- assets/svg/home.svg | 5 +- assets/svg/pencil.svg | 2 +- assets/svg/share.svg | 8 +- .../surveillance_cameras/custom_theme.css | 12 ++ .../surveillance_cameras.json | 3 + createLayouts.ts | 8 +- css/mobile.css | 10 +- css/openinghourstable.css | 25 ++-- css/tabbedComponent.css | 42 +++++-- css/tagrendering.css | 27 ++++- css/userbadge.css | 22 ++-- generateIncludedImages.ts | 3 + index.css | 95 +++++++++++---- test.ts | 15 +-- 26 files changed, 402 insertions(+), 152 deletions(-) create mode 100644 UI/Input/Direction.ts create mode 100644 assets/themes/surveillance_cameras/custom_theme.css diff --git a/InitUiElements.ts b/InitUiElements.ts index 8310b0c..faa8dbb 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -110,6 +110,16 @@ export class InitUiElements { InitUiElements.setupAllLayerElements(); + if (layoutToUse.customCss !== undefined) { + var head = document.getElementsByTagName('head')[0]; + var link = document.createElement('link'); + link.id = "customCss"; + link.rel = 'stylesheet'; + link.type = 'text/css'; + link.href = layoutToUse.customCss; + link.media = 'all'; + head.appendChild(link); + } function updateFavs() { const favs = State.state.favouriteLayers.data ?? []; @@ -269,7 +279,7 @@ export class InitUiElements { ] if (State.state.featureSwitchShareScreen.data) { - tabs.push({header: Svg.share_img, content: new ShareScreen()}); + tabs.push({header: Svg.share, content: new ShareScreen()}); } if (State.state.featureSwitchMoreQuests.data) { @@ -282,7 +292,7 @@ export class InitUiElements { tabs.push({ - header: Svg.help_img, + header: Svg.help , content: new VariableUiElement(State.state.osmConnection.userDetails.map(userdetails => { if (userdetails.csCount < State.userJourney.mapCompleteHelpUnlock) { return "" @@ -303,8 +313,8 @@ export class InitUiElements { const fullOptions = this.CreateWelcomePane(); - const help = Svg.help_ui().SetClass("open-welcome-button"); - const close = Svg.close_ui().SetClass("close-welcome-button"); + const help = Svg.help_svg().SetClass("open-welcome-button"); + const close = Svg.close_svg().SetClass("close-welcome-button"); const checkbox = new CheckBox( new Combine([ close, @@ -328,7 +338,7 @@ export class InitUiElements { const fullOptions2 = this.CreateWelcomePane(); State.state.fullScreenMessage.setData(fullOptions2) - Svg.help_ui() + Svg.help_svg() .SetClass("open-welcome-button") .SetClass("shadow") .onClick(() => { @@ -366,14 +376,14 @@ export class InitUiElements { } layerControlPanel.SetStyle("display:block;padding:1em;border-radius:1em;"); - const closeButton = Svg.close_ui().SetClass("layer-selection-toggle").SetStyle(" background: #e5f5ff;") + const closeButton = Svg.close_svg().SetClass("layer-selection-toggle").SetStyle(" background: var(--subtle-detail-color);") const checkbox = new CheckBox( new Combine([ closeButton, layerControlPanel]).SetStyle("display:flex;flex-direction:row;") .SetClass("hidden-on-mobile") , - Svg.layers_ui().SetClass("layer-selection-toggle"), + Svg.layers_svg().SetClass("layer-selection-toggle"), State.state.layerControlIsOpened ); diff --git a/Svg.ts b/Svg.ts index 7a11237..a3a3de1 100644 --- a/Svg.ts +++ b/Svg.ts @@ -4,160 +4,199 @@ import {FixedUiElement} from "./UI/Base/FixedUiElement"; export default class Svg { - public static add = " image/svg+xml " + public static add = " image/svg+xml " public static add_img = Img.AsImageElement(Svg.add) + public static add_svg() { return new FixedUiElement(Svg.add);} public static add_ui() { return new FixedUiElement(Svg.add_img);} - public static addSmall = " image/svg+xml " + public static addSmall = " image/svg+xml " public static addSmall_img = Img.AsImageElement(Svg.addSmall) + public static addSmall_svg() { return new FixedUiElement(Svg.addSmall);} public static addSmall_ui() { return new FixedUiElement(Svg.addSmall_img);} - public static ampersand = " image/svg+xml " + public static ampersand = "e image/svg+xml " public static ampersand_img = Img.AsImageElement(Svg.ampersand) + public static ampersand_svg() { return new FixedUiElement(Svg.ampersand);} public static ampersand_ui() { return new FixedUiElement(Svg.ampersand_img);} - public static arrow_left_smooth = " image/svg+xml " + public static arrow_left_smooth = " image/svg+xml " public static arrow_left_smooth_img = Img.AsImageElement(Svg.arrow_left_smooth) + public static arrow_left_smooth_svg() { return new FixedUiElement(Svg.arrow_left_smooth);} public static arrow_left_smooth_ui() { return new FixedUiElement(Svg.arrow_left_smooth_img);} - public static arrow_right_smooth = " image/svg+xml " + public static arrow_right_smooth = " image/svg+xml " public static arrow_right_smooth_img = Img.AsImageElement(Svg.arrow_right_smooth) + public static arrow_right_smooth_svg() { return new FixedUiElement(Svg.arrow_right_smooth);} public static arrow_right_smooth_ui() { return new FixedUiElement(Svg.arrow_right_smooth_img);} public static bug = " " public static bug_img = Img.AsImageElement(Svg.bug) + public static bug_svg() { return new FixedUiElement(Svg.bug);} public static bug_ui() { return new FixedUiElement(Svg.bug_img);} - public static camera_plus = " image/svg+xml " + public static camera_plus = " image/svg+xml " public static camera_plus_img = Img.AsImageElement(Svg.camera_plus) + public static camera_plus_svg() { return new FixedUiElement(Svg.camera_plus);} public static camera_plus_ui() { return new FixedUiElement(Svg.camera_plus_img);} - public static checkmark = "" + public static checkmark = "" public static checkmark_img = Img.AsImageElement(Svg.checkmark) + public static checkmark_svg() { return new FixedUiElement(Svg.checkmark);} public static checkmark_ui() { return new FixedUiElement(Svg.checkmark_img);} - public static close = " image/svg+xml " + public static close = " image/svg+xml " public static close_img = Img.AsImageElement(Svg.close) + public static close_svg() { return new FixedUiElement(Svg.close);} public static close_ui() { return new FixedUiElement(Svg.close_img);} - public static crosshair_blue_center = " image/svg+xml " + public static crosshair_blue_center = " image/svg+xml " public static crosshair_blue_center_img = Img.AsImageElement(Svg.crosshair_blue_center) + public static crosshair_blue_center_svg() { return new FixedUiElement(Svg.crosshair_blue_center);} public static crosshair_blue_center_ui() { return new FixedUiElement(Svg.crosshair_blue_center_img);} - public static crosshair_blue = " image/svg+xml " + public static crosshair_blue = " image/svg+xml " public static crosshair_blue_img = Img.AsImageElement(Svg.crosshair_blue) + public static crosshair_blue_svg() { return new FixedUiElement(Svg.crosshair_blue);} public static crosshair_blue_ui() { return new FixedUiElement(Svg.crosshair_blue_img);} - public static crosshair = " image/svg+xml " + public static crosshair = " image/svg+xml " public static crosshair_img = Img.AsImageElement(Svg.crosshair) + public static crosshair_svg() { return new FixedUiElement(Svg.crosshair);} public static crosshair_ui() { return new FixedUiElement(Svg.crosshair_img);} - public static delete_icon = " image/svg+xml " + public static delete_icon = " image/svg+xml " public static delete_icon_img = Img.AsImageElement(Svg.delete_icon) + 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 down = " image/svg+xml " + public static down = " image/svg+xml " public static down_img = Img.AsImageElement(Svg.down) + public static down_svg() { return new FixedUiElement(Svg.down);} public static down_ui() { return new FixedUiElement(Svg.down_img);} - public static envelope = " " + public static envelope = " image/svg+xml " public static envelope_img = Img.AsImageElement(Svg.envelope) + public static envelope_svg() { return new FixedUiElement(Svg.envelope);} public static envelope_ui() { return new FixedUiElement(Svg.envelope_img);} - public static floppy = " " + public static floppy = " " public static floppy_img = Img.AsImageElement(Svg.floppy) + public static floppy_svg() { return new FixedUiElement(Svg.floppy);} public static floppy_ui() { return new FixedUiElement(Svg.floppy_img);} - public static gear = "" + public static gear = " " public static gear_img = Img.AsImageElement(Svg.gear) + public static gear_svg() { return new FixedUiElement(Svg.gear);} public static gear_ui() { return new FixedUiElement(Svg.gear_img);} - public static help = " image/svg+xml " + public static help = " " public static help_img = Img.AsImageElement(Svg.help) + public static help_svg() { return new FixedUiElement(Svg.help);} public static help_ui() { return new FixedUiElement(Svg.help_img);} - public static home = " " + public static home = " " public static home_img = Img.AsImageElement(Svg.home) + public static home_svg() { return new FixedUiElement(Svg.home);} public static home_ui() { return new FixedUiElement(Svg.home_img);} - public static josm_logo = " JOSM Logotype 2019 image/svg+xml JOSM Logotype 2019 2019-08-05 Diamond00744 Public Domain " + public static josm_logo = " JOSM Logotype 2019 image/svg+xml JOSM Logotype 2019 2019-08-05 Diamond00744 Public Domain " public static josm_logo_img = Img.AsImageElement(Svg.josm_logo) + public static josm_logo_svg() { return new FixedUiElement(Svg.josm_logo);} public static josm_logo_ui() { return new FixedUiElement(Svg.josm_logo_img);} - public static layers = " image/svg+xml " + public static layers = " image/svg+xml " public static layers_img = Img.AsImageElement(Svg.layers) + public static layers_svg() { return new FixedUiElement(Svg.layers);} public static layers_ui() { return new FixedUiElement(Svg.layers_img);} - public static layersAdd = " image/svg+xml " + public static layersAdd = " image/svg+xml " public static layersAdd_img = Img.AsImageElement(Svg.layersAdd) + public static layersAdd_svg() { return new FixedUiElement(Svg.layersAdd);} public static layersAdd_ui() { return new FixedUiElement(Svg.layersAdd_img);} - public static logo = " image/svg+xml " + public static logo = " image/svg+xml " public static logo_img = Img.AsImageElement(Svg.logo) + public static logo_svg() { return new FixedUiElement(Svg.logo);} public static logo_ui() { return new FixedUiElement(Svg.logo_img);} - public static logout = " image/svg+xml " + public static logout = " image/svg+xml " public static logout_img = Img.AsImageElement(Svg.logout) + public static logout_svg() { return new FixedUiElement(Svg.logout);} public static logout_ui() { return new FixedUiElement(Svg.logout_img);} public static mapillary = "" public static mapillary_img = Img.AsImageElement(Svg.mapillary) + public static mapillary_svg() { return new FixedUiElement(Svg.mapillary);} public static mapillary_ui() { return new FixedUiElement(Svg.mapillary_img);} public static no_checkmark = " " public static no_checkmark_img = Img.AsImageElement(Svg.no_checkmark) + public static no_checkmark_svg() { return new FixedUiElement(Svg.no_checkmark);} public static no_checkmark_ui() { return new FixedUiElement(Svg.no_checkmark_img);} - public static or = " image/svg+xml " + public static or = " image/svg+xml " public static or_img = Img.AsImageElement(Svg.or) + public static or_svg() { return new FixedUiElement(Svg.or);} public static or_ui() { return new FixedUiElement(Svg.or_img);} public static osm_logo_us = "" public static osm_logo_us_img = Img.AsImageElement(Svg.osm_logo_us) + public static osm_logo_us_svg() { return new FixedUiElement(Svg.osm_logo_us);} public static osm_logo_us_ui() { return new FixedUiElement(Svg.osm_logo_us_img);} - public static osm_logo = " OpenStreetMap logo 2011 image/svg+xml OpenStreetMap logo 2011 Ken Vermette April 2011 OpenStreetMap.org Replacement logo for OpenStreetMap Foundation OSM openstreetmap logo http://wiki.openstreetmap.org/wiki/File:Public-images-osm_logo.svg 010110010011010110010011 010110010011010110010011 " + public static osm_logo = " OpenStreetMap logo 2011 image/svg+xml OpenStreetMap logo 2011 Ken Vermette April 2011 OpenStreetMap.org Replacement logo for OpenStreetMap Foundation OSM openstreetmap logo http://wiki.openstreetmap.org/wiki/File:Public-images-osm_logo.svg 010110010011010110010011 010110010011010110010011 " public static osm_logo_img = Img.AsImageElement(Svg.osm_logo) + public static osm_logo_svg() { return new FixedUiElement(Svg.osm_logo);} public static osm_logo_ui() { return new FixedUiElement(Svg.osm_logo_img);} - public static pencil = " " + public static pencil = " " public static pencil_img = Img.AsImageElement(Svg.pencil) + public static pencil_svg() { return new FixedUiElement(Svg.pencil);} public static pencil_ui() { return new FixedUiElement(Svg.pencil_img);} - public static pop_out = " Svg Vector Icons : http://www.onlinewebfonts.com/icon " + public static pop_out = " Svg Vector Icons : http://www.onlinewebfonts.com/icon " public static pop_out_img = Img.AsImageElement(Svg.pop_out) + public static pop_out_svg() { return new FixedUiElement(Svg.pop_out);} public static pop_out_ui() { return new FixedUiElement(Svg.pop_out_img);} - public static reload = " " + public static reload = " " public static reload_img = Img.AsImageElement(Svg.reload) + public static reload_svg() { return new FixedUiElement(Svg.reload);} public static reload_ui() { return new FixedUiElement(Svg.reload_img);} - public static search = " " + public static search = " " public static search_img = Img.AsImageElement(Svg.search) + public static search_svg() { return new FixedUiElement(Svg.search);} public static search_ui() { return new FixedUiElement(Svg.search_img);} - public static share = " image/svg+xml " + public static share = " image/svg+xml " public static share_img = Img.AsImageElement(Svg.share) + public static share_svg() { return new FixedUiElement(Svg.share);} public static share_ui() { return new FixedUiElement(Svg.share_img);} - public static star = " Created by potrace 1.15, written by Peter Selinger 2001-2017 " + public static star = " Created by potrace 1.15, written by Peter Selinger 2001-2017 " public static star_img = Img.AsImageElement(Svg.star) + public static star_svg() { return new FixedUiElement(Svg.star);} public static star_ui() { return new FixedUiElement(Svg.star_img);} - public static statistics = " Svg Vector Icons : http://www.onlinewebfonts.com/icon " + public static statistics = " Svg Vector Icons : http://www.onlinewebfonts.com/icon " public static statistics_img = Img.AsImageElement(Svg.statistics) + public static statistics_svg() { return new FixedUiElement(Svg.statistics);} public static statistics_ui() { return new FixedUiElement(Svg.statistics_img);} - public static up = " " + public static up = " " public static up_img = Img.AsImageElement(Svg.up) + public static up_svg() { return new FixedUiElement(Svg.up);} public static up_ui() { return new FixedUiElement(Svg.up_img);} - public static wikimedia_commons_white = " Wikimedia Commons Logo " + public static wikimedia_commons_white = " Wikimedia Commons Logo " public static wikimedia_commons_white_img = Img.AsImageElement(Svg.wikimedia_commons_white) + public static wikimedia_commons_white_svg() { return new FixedUiElement(Svg.wikimedia_commons_white);} public static wikimedia_commons_white_ui() { return new FixedUiElement(Svg.wikimedia_commons_white_img);} - public static wikipedia = " Wikipedia logo version 2" + public static wikipedia = " Wikipedia logo version 2" public static wikipedia_img = Img.AsImageElement(Svg.wikipedia) + public static wikipedia_svg() { return new FixedUiElement(Svg.wikipedia);} public static wikipedia_ui() { return new FixedUiElement(Svg.wikipedia_img);} } diff --git a/UI/FullScreenMessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts index bf12c48..6fe9948 100644 --- a/UI/FullScreenMessageBoxHandler.ts +++ b/UI/FullScreenMessageBoxHandler.ts @@ -27,7 +27,8 @@ export class FullScreenMessageBox extends UIElement { "overflow-y: auto;" + "max-width:100vw;" + "overflow-x:hidden;" + - "background:white;" + "background:var(--background-color);" + + "color: var(--foreground-color);" ); }); diff --git a/UI/Image/ImageUploadFlow.ts b/UI/Image/ImageUploadFlow.ts index 2fb61bc..6836771 100644 --- a/UI/Image/ImageUploadFlow.ts +++ b/UI/Image/ImageUploadFlow.ts @@ -44,7 +44,7 @@ export class ImageUploadFlow extends UIElement { this._licensePicker = licensePicker; this._selectedLicence = licensePicker.GetValue(); - this._connectButton = new Combine([t.pleaseLogin]) + this._connectButton = t.pleaseLogin.Clone() .onClick(() => State.state.osmConnection.AttemptLogin()) .SetClass("login-button-friendly"); @@ -101,7 +101,7 @@ export class ImageUploadFlow extends UIElement { ]); const label = new Combine([ - Svg.camera_plus_ui().SetStyle("width: 36px;height: 36px;padding: 0.1em;margin-top: 5px;border-radius: 0;float: left;display:block"), + Svg.camera_plus_svg().SetStyle("width: 36px;height: 36px;padding: 0.1em;margin-top: 5px;border-radius: 0;float: left;display:block"), Translations.t.image.addPicture .SetStyle("width:max-content;font-size: 28px;" + "font-weight: bold;" + @@ -115,7 +115,7 @@ export class ImageUploadFlow extends UIElement { "cursor:pointer;" + "padding: 0.5em;" + "border-radius: 1em;" + - "border: 3px solid black;" + + "border: 3px solid var(--popup-border);" + "box-sizing:border-box;") const actualInputElement = diff --git a/UI/Input/Direction.ts b/UI/Input/Direction.ts new file mode 100644 index 0000000..d7dc5dc --- /dev/null +++ b/UI/Input/Direction.ts @@ -0,0 +1,39 @@ +import {InputElement} from "./InputElement"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import Combine from "../Base/Combine"; +import {FixedUiElement} from "../Base/FixedUiElement"; + +/** + * Selects a direction in degrees + */ +export default class Direction extends InputElement{ + + private readonly value: UIEventSource; + public readonly IsSelected: UIEventSource = new UIEventSource(false); + + constructor(value?: UIEventSource) { + super(); + this.value = value ?? new UIEventSource(undefined); + } + + + GetValue(): UIEventSource { + return this.value; + } + + InnerRender(): string { + return new Combine([ + new FixedUiElement("").SetStyle( + "position: absolute;top: calc(50% - 0.5em);left: calc(50% - 0.5em);width: 1em;height: 1em;background: red;border-radius: 1em"), + + ]) + .SetStyle("position:relative;display:block;width: min(100%, 25em); padding-top: min(100% , 25em); background:white; border: 1px solid black; border-radius: 999em") + .Render(); + } + + + IsValid(t: number): boolean { + return t >= 0 && t <= 360; + } + +} \ No newline at end of file diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index 931b570..fce78e6 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -38,7 +38,7 @@ export default class EditableTagRendering extends UIElement { if(State.state.featureSwitchUserbadge.data){ this._editButton = - Svg.pencil_ui().SetClass("edit-button") + Svg.pencil_svg().SetClass("edit-button") .onClick(() => { self._editMode.setData(true); }); diff --git a/UI/UserBadge.ts b/UI/UserBadge.ts index 49e8acf..6a84fb3 100644 --- a/UI/UserBadge.ts +++ b/UI/UserBadge.ts @@ -34,7 +34,7 @@ export class UserBadge extends UIElement { .SetClass("userbadge-login") .onClick(() => State.state.osmConnection.AttemptLogin()); this._logout = - Svg.logout_ui() + Svg.logout_svg() .onClick(() => { State.state.osmConnection.LogOut(); }); @@ -52,7 +52,7 @@ export class UserBadge extends UIElement { this._homeButton = new VariableUiElement( this._userDetails.map((userinfo) => { if (userinfo.home) { - return Svg.home_img; + return Svg.home; } return ""; }) @@ -75,7 +75,7 @@ export class UserBadge extends UIElement { let messageSpan: UIElement = new Link( - new Combine([Svg.envelope_img, "" + user.totalMessages]), + new Combine([Svg.envelope, "" + user.totalMessages]), 'https://www.openstreetmap.org/messages/inbox', true ) @@ -83,7 +83,7 @@ export class UserBadge extends UIElement { if (user.unreadMessages > 0) { messageSpan = new Link( - new Combine([Svg.envelope_img, "" + user.unreadMessages]), + new Combine([Svg.envelope, "" + user.unreadMessages]), 'https://www.openstreetmap.org/messages/inbox', true ).SetClass("alert") @@ -104,7 +104,7 @@ export class UserBadge extends UIElement { } const settings = - new Link(Svg.gear_ui(), + new Link(Svg.gear_svg(), `https://www.openstreetmap.org/user/${encodeURIComponent(user.name)}/account`, true) @@ -124,7 +124,7 @@ export class UserBadge extends UIElement { const csCount = new Link( - new Combine([Svg.star_img, "" + user.csCount]), + new Combine([Svg.star, "" + user.csCount]), `https://www.openstreetmap.org/user/${user.name}/history`, true); diff --git a/assets/svg/ampersand.svg b/assets/svg/ampersand.svg index 525a1ef..f2df861 100644 --- a/assets/svg/ampersand.svg +++ b/assets/svg/ampersand.svg @@ -1,4 +1,4 @@ - +e \ No newline at end of file + \ No newline at end of file diff --git a/assets/svg/envelope.svg b/assets/svg/envelope.svg index 8728151..74923d3 100644 --- a/assets/svg/envelope.svg +++ b/assets/svg/envelope.svg @@ -1,4 +1,47 @@ - - - - \ No newline at end of file + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/assets/svg/gear.svg b/assets/svg/gear.svg index 302fac4..6142f34 100644 --- a/assets/svg/gear.svg +++ b/assets/svg/gear.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + \ No newline at end of file diff --git a/assets/svg/help.svg b/assets/svg/help.svg index 1f6bad7..b6bd681 100644 --- a/assets/svg/help.svg +++ b/assets/svg/help.svg @@ -8,33 +8,21 @@ id="svg11382" height="900" width="900" + viewBox="0 0 900 900" version="1.0"> - - - - image/svg+xml - - - - - - + transform="matrix(0.90103258,0,0,0.90103258,112.84058,-1.9060177)" + > diff --git a/assets/svg/home.svg b/assets/svg/home.svg index b9f31cf..c27eb53 100644 --- a/assets/svg/home.svg +++ b/assets/svg/home.svg @@ -1,3 +1,6 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/assets/svg/pencil.svg b/assets/svg/pencil.svg index 6f9610f..d41508f 100644 --- a/assets/svg/pencil.svg +++ b/assets/svg/pencil.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/assets/svg/share.svg b/assets/svg/share.svg index d70d175..83f599f 100644 --- a/assets/svg/share.svg +++ b/assets/svg/share.svg @@ -73,25 +73,25 @@ id="layer1" transform="translate(0,-270.54165)"> ` + } } const og = ` diff --git a/css/mobile.css b/css/mobile.css index f4c5839..6ee0708 100644 --- a/css/mobile.css +++ b/css/mobile.css @@ -4,6 +4,8 @@ Contains tweaks for small screens .only-on-mobile { display: none; + background-color: var(--background-color); + color: var(--foreground-color); } @media only screen and (max-width: 600px), only screen and (max-height: 600px) { @@ -14,6 +16,8 @@ Contains tweaks for small screens .only-on-mobile { display: unset; + background-color: var(--background-color); + color: var(--foreground-color); } .hidden-on-mobile { @@ -22,6 +26,8 @@ Contains tweaks for small screens #messagesbox { display: none; + background-color: var(--background-color); + color: var(--foreground-color); } #help-button-mobile{ @@ -57,15 +63,15 @@ Contains tweaks for small screens #messagesboxmobile { display: block; + position: absolute; z-index: 10000; - background-color: white; width: 100vw; } #welcomeMessage { display: inline-block; - background-color: white; + background-color: var(--background-color); border-radius: 0; width: 100%; max-width: 100%; diff --git a/css/openinghourstable.css b/css/openinghourstable.css index 5034cff..4d04de0 100644 --- a/css/openinghourstable.css +++ b/css/openinghourstable.css @@ -5,7 +5,6 @@ text-align: center; word-break: normal; } - .oh-table th { padding: 0; margin: 0; @@ -34,22 +33,22 @@ } .oh-timecell:hover { - background-color: #92b1ff !important; + background-color: var(--catch-detail-color) !important; } .oh-timecell-selected { - background-color: #0048ff; + background-color: var(--catch-detail-color); } .oh-timecell-half { border-bottom: 1px solid #ddd; - background-color: aliceblue; + background-color: var(--subtle-detail-color); } .oh-timecell-half.oh-timecell-selected { - background-color: #0048ff; + background-color: var(--catch-detail-color); } .oh-table tr { @@ -75,12 +74,12 @@ } .oh-timecol-selected { - border-right: #0048ff; + border-right: var(--catch-detail-color); } .oh-timecol-selected > span { - background-color: #0048ff; - color: white; + background-color: var(--catch-detail-color); + color: var(--background-color); width: 100%; display: block; } @@ -96,11 +95,11 @@ } .oh-timerow-selected .oh-timecell-0 { - border-left: 10px solid #0048ff !important; + border-left: 10px solid var(--catch-detail-color) !important; } .oh-timerow-selected .oh-timecell-6 { - border-right: 10px solid #0048ff !important; + border-right: 10px solid var(--catch-detail-color) !important; } @@ -117,7 +116,7 @@ top: 0; left: 0; width: calc(100% - 4px); - background: #0048ff; + background: var(--catch-detail-color); z-index: 1; box-sizing: border-box; } @@ -276,7 +275,7 @@ .ohviz-today { - background-color: #e5f5ff; + background-color: var(--subtle-detail-color); } .ohviz-weekday { @@ -290,7 +289,7 @@ } .ohviz-container { - border: 0.5em solid #e5f5ff; + border: 0.5em solid var(--subtle-detail-color); border-radius: 1em; display: block; } diff --git a/css/tabbedComponent.css b/css/tabbedComponent.css index 741c7ba..d895fc9 100644 --- a/css/tabbedComponent.css +++ b/css/tabbedComponent.css @@ -7,7 +7,7 @@ flex-wrap: nowrap; justify-content: flex-start; align-items: start; - background-color: white; + background-color: var(--background-color); max-width: 100vw; overflow-x: auto; } @@ -21,10 +21,19 @@ margin:auto; } +.tab-single-header svg { + height: 3em; + max-width: 3em; + padding: 0.5em; + display:block; + margin:auto; +} + .tab-content { z-index: 5002; - background-color: white; + background-color: var(--background-color); + color: var(--foreground-color); position: relative; padding: 1em; display: inline-block; @@ -41,19 +50,36 @@ } .tab-active { - background-color: white; + background-color: var(--background-color); + color: var(--foreground-color); z-index: 5001; - box-shadow: 0 0 10px black; - border: 1px solid white; + box-shadow: 0 0 10px var(--shadow-color); + border: 1px solid var(--background-color); min-width: 4em; } +.tab-active svg { + fill: var(--foreground-color); + stroke: var(--foreground-color); +} + .tab-non-active { - background-color: #e5f5ff; + background-color: var(--subtle-detail-color); + color: var(--foreground-color); opacity: 0.5; border-left: 1px solid gray; - border-right:1px solid gray; - border-top: 1px solid gray; + border-right: 1px solid gray; + border-top: 1px solid gray; border-bottom: 1px solid lightgray; min-width: 4em; } + +.tab-non-active svg { + fill: var(--foreground-color) !important; + stroke: var(--foreground-color) !important; +} + +.tab-non-active svg path{ + fill: var(--foreground-color) !important; + stroke: var(--foreground-color) !important; +} diff --git a/css/tagrendering.css b/css/tagrendering.css index 7c08861..705202d 100644 --- a/css/tagrendering.css +++ b/css/tagrendering.css @@ -30,7 +30,7 @@ .question { display: block; margin-top: 1em; - background-color: #e5f5ff; + background-color: var(--subtle-detail-color); padding: 1em; border-radius: 1em; font-size: larger; @@ -72,7 +72,7 @@ input:checked + label .question-option-with-border { .save { display: inline-block; border: solid white 2px; - background-color: #3a3aeb; + background-color: var(--catch-detail-color); color: white; padding: 0.2em 0.6em; font-size: x-large; @@ -90,8 +90,8 @@ input:checked + label .question-option-with-border { .login-button-friendly { display: inline-block; border: solid white 2px; - background-color: #3a3aeb; - color: white; + background-color:var(--catch-detail-color); + color: var(--catch-detail-color-contrast); padding: 0.2em 0.6em; font-size: large; font-weight: bold; @@ -115,7 +115,24 @@ input:checked + label .question-option-with-border { height: 1.3em; padding: 0.5em; border-radius: 0.65em; - border: solid black 1px; + border: solid var(--popup-border) 1px; font-size: medium; float: right; +} + +.edit-button svg { + width: 1.3em; + height: 1.3em; + padding: 0.5em; + border-radius: 0.65em; + border: solid var(--popup-border) 1px; + stroke: var(--popup-border) !important; + fill: var(--popup-border) !important; + font-size: medium; + float: right; +} + +.edit-button svg path{ + stroke: var(--popup-border) !important; + fill: var(--popup-border) !important; } \ No newline at end of file diff --git a/css/userbadge.css b/css/userbadge.css index 81ac712..49ddab2 100644 --- a/css/userbadge.css +++ b/css/userbadge.css @@ -1,6 +1,7 @@ #userbadge { display: inline-block; - background-color: white; + background-color: var(--background-color); + color: var(--foreground-color); -webkit-border-radius: 2em; -moz-border-radius: 2em; border-radius: 2em; @@ -15,7 +16,7 @@ #userbadge a { text-decoration: none; - color: black; + color: var(--foreground-color); } @@ -42,14 +43,19 @@ margin-bottom: 0.2em; } -.userstats img { +.userstats svg { width: 1em; height: 1em; - fill: black; - border-radius: 0; + border-radius: 0; display: block; } +.userstats img { + width: 1em; + height: 1em; + border-radius: 0; + display: block; +} #profile-pic { float: left; @@ -76,7 +82,8 @@ height: 2.2em; /*SHould equal profile-pic height - padding*/ z-index: 5000; text-align: left; - background-color: white; + background-color: var(--background-color); + color: var(--foreground-color); background-size: 100%; line-height: 0.75em; @@ -88,7 +95,8 @@ .userbadge-login { font-weight: bold; font-size: large; - background-color: #e5f5ff !important; + background-color: var(--subtle-detail-color) !important; + color: var(--subtle-detail-color-contrast); height:3em; display: inline-block; diff --git a/generateIncludedImages.ts b/generateIncludedImages.ts index 1237403..73a8251 100644 --- a/generateIncludedImages.ts +++ b/generateIncludedImages.ts @@ -14,6 +14,8 @@ function genImages() { } const svg = fs.readFileSync("./assets/svg/" + path, "utf-8") + .replace(/<\?xml.*?>/, "") + .replace(/fill: ?none;/g,"fill: none !important;") // This is such a brittle hack... .replace(/\n/g, " ") .replace(/\r/g, "") .replace(/\\/g, "\\") @@ -22,6 +24,7 @@ function genImages() { .replace(/[ -]/g, "_"); module += ` public static ${name} = "${svg}"\n` module += ` public static ${name}_img = Img.AsImageElement(Svg.${name})\n` + module += ` public static ${name}_svg() { return new FixedUiElement(Svg.${name});}\n` module += ` public static ${name}_ui() { return new FixedUiElement(Svg.${name}_img);}\n\n` } module += "}\n"; diff --git a/index.css b/index.css index 8df3e9a..e0bd4e2 100644 --- a/index.css +++ b/index.css @@ -1,11 +1,33 @@ +:root { + --subtle-detail-color: #e5f5ff; + --subtle-detail-color-contrast: black; + --catch-detail-color: #3a3aeb; + --catch-detail-color-contrast: white; + --alert-color: #fee4d1; + --background-color: white; + --foreground-color: black; + --popup-border: white; + --shadow-color: #00000066 +} + html, body { height: 100%; margin: 0; padding: 0; + background-color: var(--background-color); + color: var(--foreground-color); + font-family: 'Helvetica Neue', Arial, sans-serif; } -body { - font-family: 'Helvetica Neue', Arial, sans-serif; + +svg { + fill: var(--foreground-color) !important; + stroke: var(--foreground-color) !important; +} + +svg path { + fill: var(--foreground-color) !important; + stroke: var(--foreground-color) !important; } @@ -13,6 +35,17 @@ body { height: 100%; } +.leaflet-popup-content-wrapper { + background-color: var(--background-color); + color: var(--foreground-color); + + border: 2px solid var(--popup-border) +} + +.leaflet-container { + background-color: var(--background-color) !important; +} + #geolocate-button { position: absolute; bottom: 25px; @@ -39,22 +72,25 @@ body { bottom: 1em; left: 1em; z-index: 9000; - background-color: white; + background-color: var(--background-color); + color: var(--foreground-color); + border-radius: 1em; cursor: pointer; - box-shadow: 0 0 10px #00000066; + box-shadow: 0 0 10px var(--shadow-color); } .layer-selection-toggle { - border-top-left-radius: 1em; - border-bottom-left-radius: 1em; + border-radius: 1em; display: flex; flex-direction: column-reverse; + background: var(--subtle-detail-color); } -.layer-selection-toggle img { +.layer-selection-toggle svg { display: block; width: 2em; + height: 2em; padding: 1em; } @@ -62,7 +98,7 @@ body { .alert { - background-color: #fee4d1; + background-color: var(--alert-color); font-weight: bold; border-radius: 1em; margin: 0.25em; @@ -82,7 +118,7 @@ body { } .shadow { - box-shadow: 0 0 10px #00000066; + box-shadow: 0 0 10px var(--shadow-color); } .title-font span { @@ -91,7 +127,8 @@ body { } .soft { - background-color: #e5f5ff; + background-color: var(--subtle-detail-color); + color: var(--subtle-detail-color-contrast); font-weight: bold; border-radius: 1em; margin: 0.25em; @@ -137,7 +174,9 @@ body { #searchbox { display: inline-block; text-align: left; - background-color: white; + background-color: var(--background-color); + color: var(--foreground-color); + transition: all 500ms linear; pointer-events: all; border-radius: 1.3em; @@ -170,6 +209,7 @@ body { font-size: large; width: 100%; box-sizing: border-box; + color: var(--foreground-color); } .search-go img { @@ -215,13 +255,16 @@ body { overflow-y: auto; border-top-right-radius: 1em; border-bottom-right-radius: 1em; + background-color: var(--background-color); + color: var(--foreground-color); } .close-welcome-button { position: absolute; display: inline-block; height: 100%; - background-color: #e5f5ff; + background-color: var(--subtle-detail-color); + color: var(--subtle-detail-color-contrast); box-sizing: border-box; width: 4em; padding: 1em; @@ -229,22 +272,26 @@ body { border-bottom-left-radius: 1em; } -.close-welcome-button img { +.close-welcome-button svg { width: 2em; + height: 2em; } .open-welcome-button { display: inline-block; box-sizing: border-box; - background-color: white; + background: var(--subtle-detail-color); + color: var(--foreground-color); + height: 4em; width: 4em; padding: 1em; border-radius: 1em; } -.open-welcome-button img { +.open-welcome-button svg { width: 2em; + height: 2em; } #messagesbox { @@ -252,9 +299,11 @@ body { position: relative; padding: 0; pointer-events: all; - box-shadow: 0 0 10px #00000066; + box-shadow: 0 0 10px var(--shadow-color); border-radius: 1em; - width: min-content + width: min-content; + background-color: var(--background-color); + color: var(--foreground-color); } @@ -273,7 +322,9 @@ body { pointer-events: none; opacity: 1; - background-color: white; + background-color: var(--background-color); + color: var(--foreground-color); + transition: opacity 500ms linear; @@ -357,7 +408,9 @@ body { .iframe-escape { - background-color: white; + background-color: var(--background-color); + color: var(--foreground-color); + border-radius: 2em; display: block; width: min-content; @@ -376,11 +429,11 @@ body { flex-direction: row; font-size: large; margin: 0.5em; - background-color: #e5f5ff; + background-color: var(--subtle-detail-color); + color: var(--subtle-detail-color-contrast); border-radius: 1em; align-items: center; text-decoration: none; - color: black; } diff --git a/test.ts b/test.ts index 77feaa5..f1b3381 100644 --- a/test.ts +++ b/test.ts @@ -1,20 +1,11 @@ -/* +//* -import {UIEventSource} from "./Logic/UIEventSource"; -import {FeatureInfoBox} from "./UI/Popup/FeatureInfoBox"; -import SharedLayers from "./Customizations/SharedLayers"; +import Direction from "./UI/Input/Direction"; -const tags = { - mapillary: "wweALGY5g8_T8UjGkcWCfw", - wikimedia_commons: "File:Boekenkast Sint-Lodewijks.jpg" -} -const src = new UIEventSource(tags); +new Direction().AttachTo("maindiv") -new FeatureInfoBox(src, SharedLayers.sharedLayers["ghost_bike"]).AttachTo('maindiv'); -//const subs = new SubstitutedTranslation(new Translation({"nl":"NL {image_carousel()} {image_upload()}"}), src) -//subs.AttachTo("maindiv") /*/ From c86f4e4aff99fccd62d430e19b5cc83289fc8469 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 14 Nov 2020 03:26:09 +0100 Subject: [PATCH 04/20] More css tweaks --- InitUiElements.ts | 9 +-------- State.ts | 2 +- Svg.ts | 2 +- Utils.ts | 11 +++++++++++ assets/svg/checkmark.svg | 2 +- .../surveillance_cameras/custom_theme.css | 6 +++--- .../surveillance_cameras.json | 2 +- css/tagrendering.css | 19 ++++++++++--------- index.css | 3 ++- index.ts | 5 +++++ 10 files changed, 36 insertions(+), 25 deletions(-) diff --git a/InitUiElements.ts b/InitUiElements.ts index faa8dbb..286d294 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -111,14 +111,7 @@ export class InitUiElements { InitUiElements.setupAllLayerElements(); if (layoutToUse.customCss !== undefined) { - var head = document.getElementsByTagName('head')[0]; - var link = document.createElement('link'); - link.id = "customCss"; - link.rel = 'stylesheet'; - link.type = 'text/css'; - link.href = layoutToUse.customCss; - link.media = 'all'; - head.appendChild(link); + Utils.LoadCustomCss(layoutToUse.customCss); } function updateFavs() { diff --git a/State.ts b/State.ts index 5d7b63f..f3da376 100644 --- a/State.ts +++ b/State.ts @@ -22,7 +22,7 @@ export default class State { // The singleton of the global state public static state: State; - public static vNumber = "0.1.2f"; + public static vNumber = "0.1.3"; // The user journey states thresholds when a new feature gets unlocked public static userJourney = { diff --git a/Svg.ts b/Svg.ts index a3a3de1..1053b48 100644 --- a/Svg.ts +++ b/Svg.ts @@ -39,7 +39,7 @@ export default class Svg { public static camera_plus_svg() { return new FixedUiElement(Svg.camera_plus);} public static camera_plus_ui() { return new FixedUiElement(Svg.camera_plus_img);} - public static checkmark = "" + public static checkmark = "" public static checkmark_img = Img.AsImageElement(Svg.checkmark) public static checkmark_svg() { return new FixedUiElement(Svg.checkmark);} public static checkmark_ui() { return new FixedUiElement(Svg.checkmark_img);} diff --git a/Utils.ts b/Utils.ts index 9e5cdf7..e6c1084 100644 --- a/Utils.ts +++ b/Utils.ts @@ -154,5 +154,16 @@ export class Utils { }); } + + public static LoadCustomCss(location: string){ + var head = document.getElementsByTagName('head')[0]; + var link = document.createElement('link'); + link.id = "customCss"; + link.rel = 'stylesheet'; + link.type = 'text/css'; + link.href = location; + link.media = 'all'; + head.appendChild(link); + } } diff --git a/assets/svg/checkmark.svg b/assets/svg/checkmark.svg index 811295e..e206d63 100644 --- a/assets/svg/checkmark.svg +++ b/assets/svg/checkmark.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/themes/surveillance_cameras/custom_theme.css b/assets/themes/surveillance_cameras/custom_theme.css index ec4d17f..afbb787 100644 --- a/assets/themes/surveillance_cameras/custom_theme.css +++ b/assets/themes/surveillance_cameras/custom_theme.css @@ -1,12 +1,12 @@ html { - --subtle-detail-color: #9d9d9d !important; - --subtle-detail-color-contrast: #00ff00 !important; + --subtle-detail-color: #070 !important; + --subtle-detail-color-contrast: #white !important; --popup-border: #00ff00 !important; --catch-detail-color: #00ff00 !important; --catch-detail-color-contrast: black !important; --alert-color: #eb00ff !important; --background-color: black !important; --foreground-color: white !important; - --shadow-color: white !important; + --shadow-color: #0f0 !important; } diff --git a/assets/themes/surveillance_cameras/surveillance_cameras.json b/assets/themes/surveillance_cameras/surveillance_cameras.json index dbf40f7..7d77260 100644 --- a/assets/themes/surveillance_cameras/surveillance_cameras.json +++ b/assets/themes/surveillance_cameras/surveillance_cameras.json @@ -156,7 +156,7 @@ "nl": "Wat wordt hier precies bewaakt?" }, "freeform": { - "key": "surveillance:type" + "key": "surveillance:zone" }, "render": { "en": " Surveills a {surveillance:zone}", diff --git a/css/tagrendering.css b/css/tagrendering.css index 705202d..7694687 100644 --- a/css/tagrendering.css +++ b/css/tagrendering.css @@ -31,6 +31,7 @@ display: block; margin-top: 1em; background-color: var(--subtle-detail-color); + color: var(--subtle-detail-color-contrast); padding: 1em; border-radius: 1em; font-size: larger; @@ -65,15 +66,15 @@ } input:checked + label .question-option-with-border { - border: 2px solid black; + border: 2px solid var(--subtle-detail-color-contrast); } .save { display: inline-block; - border: solid white 2px; + border: solid var(--catch-detail-color-contrast) 2px; background-color: var(--catch-detail-color); - color: white; + color: var(--catch-detail-color-contrast); padding: 0.2em 0.6em; font-size: x-large; font-weight: bold; @@ -89,9 +90,9 @@ input:checked + label .question-option-with-border { .login-button-friendly { display: inline-block; - border: solid white 2px; background-color:var(--catch-detail-color); color: var(--catch-detail-color-contrast); + border: solid var(--catch-detail-color-contrast) 2px; padding: 0.2em 0.6em; font-size: large; font-weight: bold; @@ -125,14 +126,14 @@ input:checked + label .question-option-with-border { height: 1.3em; padding: 0.5em; border-radius: 0.65em; - border: solid var(--popup-border) 1px; - stroke: var(--popup-border) !important; - fill: var(--popup-border) !important; + border: solid var(--foreground-color) 1px; + stroke: var(--foreground-color) !important; + fill: var(--foreground-color) !important; font-size: medium; float: right; } .edit-button svg path{ - stroke: var(--popup-border) !important; - fill: var(--popup-border) !important; + stroke: var(--foreground-color) !important; + fill: var(--foreground-color) !important; } \ No newline at end of file diff --git a/index.css b/index.css index e0bd4e2..54dc235 100644 --- a/index.css +++ b/index.css @@ -39,7 +39,8 @@ svg path { background-color: var(--background-color); color: var(--foreground-color); - border: 2px solid var(--popup-border) + border: 2px solid var(--popup-border); + box-shadow: 0 3px 14px var(--shadow-color) !important; } .leaflet-container { diff --git a/index.ts b/index.ts index 62b53f8..22098bc 100644 --- a/index.ts +++ b/index.ts @@ -5,6 +5,7 @@ import {QueryParameters} from "./Logic/Web/QueryParameters"; import {UIEventSource} from "./Logic/UIEventSource"; import * as $ from "jquery"; import LayoutConfig from "./Customizations/JSON/LayoutConfig"; +import {Utils} from "./Utils"; let defaultLayout = "bookcases" // --------------------- Special actions based on the parameters ----------------- @@ -29,6 +30,10 @@ if(location.href.indexOf("pietervdvn.github.io") >= 0){ defaultLayout = "bookcases" } +const customCssQP = QueryParameters.GetQueryParameter("custom-css", "", "If specified, the custom css from the given link will be loaded additionaly"); +if(customCssQP.data !== undefined && customCssQP.data !== ""){ + Utils.LoadCustomCss(customCssQP.data); +} let testing: UIEventSource; From 9978879536872c45da193abe8217d2b5dfb9f128 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 15 Nov 2020 00:27:55 +0100 Subject: [PATCH 05/20] Further theme tweaks, attempts to optimize --- InitUiElements.ts | 2 +- Logic/FilteredLayer.ts | 19 +++--- UI/FullScreenMessageBoxHandler.ts | 60 +++++++++---------- UI/Popup/FeatureInfoBox.ts | 1 + Utils.ts | 1 + .../surveillance_cameras/custom_theme.css | 4 +- .../surveillance_cameras.json | 9 ++- index.css | 18 ++++-- 8 files changed, 62 insertions(+), 52 deletions(-) diff --git a/InitUiElements.ts b/InitUiElements.ts index 286d294..d6ba7b9 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -475,7 +475,7 @@ export class InitUiElements { throw "Layer " + layer + " was not substituted"; } - const generateInfo = (tagsES, feature) => { + const generateInfo = (tagsES) => { return new FeatureInfoBox( tagsES, diff --git a/Logic/FilteredLayer.ts b/Logic/FilteredLayer.ts index bcf71c4..c90e441 100644 --- a/Logic/FilteredLayer.ts +++ b/Logic/FilteredLayer.ts @@ -123,13 +123,9 @@ export class FilteredLayer { } }) } - - static fromDefinition( - definition, - showOnPopup: (tags: UIEventSource, feature: any) => UIElement): - FilteredLayer { - return new FilteredLayer( - definition, showOnPopup); + + static fromDefinition(definition, showOnPopup: (tags: UIEventSource, feature: any) => UIElement): FilteredLayer { + return new FilteredLayer(definition, showOnPopup); } @@ -239,7 +235,7 @@ export class FilteredLayer { } - // The data is split in two parts: the poinst and the rest + // The data is split in two parts: the point and the rest // The points get a special treatment in order to render them properly // Note that some features might get a point representation as well @@ -326,20 +322,23 @@ export class FilteredLayer { eventSource.addCallback(updateStyle); function openPopup(e) { - State.state.selectedElement.setData({feature: feature}); updateStyle() + + if (feature.geometry.type === "Point") { + State.state.selectedElement.setData({feature: feature}); return; // Points bind there own popups } const uiElement = self._showOnPopup(eventSource, feature); - L.popup({ autoPan: true, }).setContent(uiElement.Render()) .setLatLng(e.latlng) .openOn(State.state.bm.map); uiElement.Update(); + State.state.selectedElement.setData({feature: feature}); + if (e) { L.DomEvent.stop(e); // Marks the event as consumed } diff --git a/UI/FullScreenMessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts index 6fe9948..fe757ca 100644 --- a/UI/FullScreenMessageBoxHandler.ts +++ b/UI/FullScreenMessageBoxHandler.ts @@ -10,41 +10,19 @@ export class FullScreenMessageBox extends UIElement { private static readonly _toTheMap_height : string = "5em"; - private _uielement: UIElement; private readonly returnToTheMap: UIElement; constructor(onClear: (() => void)) { super(State.state.fullScreenMessage); - const self = this; - State.state.fullScreenMessage.addCallbackAndRun(uiElement => { - this._uielement = new Combine([State.state.fullScreenMessage.data]).SetStyle( - "display:block;"+ - "padding: 1em;"+ - "padding-bottom:6em;"+ - `margin-bottom:${FullScreenMessageBox._toTheMap_height};`+ - "box-sizing:border-box;"+ - `height:calc(100vh - ${FullScreenMessageBox._toTheMap_height});`+ - "overflow-y: auto;" + - "max-width:100vw;" + - "overflow-x:hidden;" + - "background:var(--background-color);" + - "color: var(--foreground-color);" - - ); - }); - - this.HideOnEmpty(true); - - State.state.fullScreenMessage.addCallback(latestData => { - if (latestData === undefined) { + State.state.fullScreenMessage.addCallbackAndRun(uiElement => { + if (uiElement === undefined) { location.hash = ""; } else { // The 'hash' makes sure a new piece of history is added. This makes the 'back-button' on android remove the popup location.hash = "#element"; } - this.Update(); - }) + }); if (window !== undefined) { window.onhashchange = function () { @@ -57,14 +35,15 @@ export class FullScreenMessageBox extends UIElement { } } + const self = this; this.returnToTheMap = new Combine([Translations.t.general.returnToTheMap.Clone().SetStyle("font-size:xx-large")]) - .SetStyle("background:#7ebc6f;" + - "position: fixed;" + - "z-index: 10000;" + - "bottom: 0;" + - "left: 0;" + - `height: ${FullScreenMessageBox._toTheMap_height};` + + .SetStyle("background:#7ebc6f;" + + "position: fixed;" + + "z-index: 10000;" + + "bottom: 0;" + + "left: 0;" + + `height: ${FullScreenMessageBox._toTheMap_height};` + "width: 100vw;" + "color: white;" + "font-weight: bold;" + @@ -88,7 +67,24 @@ export class FullScreenMessageBox extends UIElement { if (State.state.fullScreenMessage.data === undefined) { return ""; } - return new Combine([this._uielement, this.returnToTheMap]) + + const el = document.getElementById(this.id); + console.warn(el, el.style.display); + + const uielement = new Combine([State.state.fullScreenMessage.data]).SetStyle( + "display:block;" + + "padding: 1em;" + + "padding-bottom:6em;" + + `margin-bottom:${FullScreenMessageBox._toTheMap_height};` + + "box-sizing:border-box;" + + `height:calc(100vh - ${FullScreenMessageBox._toTheMap_height});` + + "overflow-y: auto;" + + "max-width:100vw;" + + "overflow-x:hidden;" + + "background:var(--background-color);" + + "color: var(--foreground-color);" + ); + return new Combine([uielement, this.returnToTheMap]) .Render(); } diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index ed66eb2..c6f59b0 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -40,6 +40,7 @@ export class FeatureInfoBox extends UIElement { } InnerRender(): string { + console.error("Inner rendering infobox for ", this._tags.data.id, this.id) return new Combine([ new Combine([this._title, this._titleIcons]) .SetClass("featureinfobox-titlebar"), diff --git a/Utils.ts b/Utils.ts index e6c1084..423ddb3 100644 --- a/Utils.ts +++ b/Utils.ts @@ -164,6 +164,7 @@ export class Utils { link.href = location; link.media = 'all'; head.appendChild(link); + console.log("Added custom layout ",location) } } diff --git a/assets/themes/surveillance_cameras/custom_theme.css b/assets/themes/surveillance_cameras/custom_theme.css index afbb787..d68e02a 100644 --- a/assets/themes/surveillance_cameras/custom_theme.css +++ b/assets/themes/surveillance_cameras/custom_theme.css @@ -1,6 +1,6 @@ html { - --subtle-detail-color: #070 !important; - --subtle-detail-color-contrast: #white !important; + --subtle-detail-color: #2c2 !important; + --subtle-detail-color-contrast: white !important; --popup-border: #00ff00 !important; --catch-detail-color: #00ff00 !important; --catch-detail-color-contrast: black !important; diff --git a/assets/themes/surveillance_cameras/surveillance_cameras.json b/assets/themes/surveillance_cameras/surveillance_cameras.json index 7d77260..c38e55e 100644 --- a/assets/themes/surveillance_cameras/surveillance_cameras.json +++ b/assets/themes/surveillance_cameras/surveillance_cameras.json @@ -111,7 +111,7 @@ }, { "question": { - "en": "What k ind of surveillance is this camera", + "en": "What kind of surveillance is this camera", "nl": "Wat soort bewaking wordt hier uitgevoerd?" }, "mappings": [ @@ -245,7 +245,12 @@ "color": { "render": "#00f" }, - "presets": [], + "presets": [ + { + "tags": ["man_made=surveillance","surveillance:type=camera"], + "title": "Surveillance camera" + } + ], "wayHandling": 2 } ], diff --git a/index.css b/index.css index 54dc235..d5c4f87 100644 --- a/index.css +++ b/index.css @@ -19,6 +19,9 @@ html, body { font-family: 'Helvetica Neue', Arial, sans-serif; } +a { + color: var(--foreground-color) +} svg { fill: var(--foreground-color) !important; @@ -38,7 +41,6 @@ svg path { .leaflet-popup-content-wrapper { background-color: var(--background-color); color: var(--foreground-color); - border: 2px solid var(--popup-border); box-shadow: 0 3px 14px var(--shadow-color) !important; } @@ -47,6 +49,12 @@ svg path { background-color: var(--background-color) !important; } +.leaflet-popup-tip { + background-color: var(--popup-border) !important; + color: var(--popup-border) !important; + box-shadow: 0 3px 14px var(--shadow-color) !important; +} + #geolocate-button { position: absolute; bottom: 25px; @@ -92,7 +100,7 @@ svg path { display: block; width: 2em; height: 2em; - padding: 1em; + padding: 0.75em; } /**************** GENERIC ****************/ @@ -284,9 +292,9 @@ svg path { background: var(--subtle-detail-color); color: var(--foreground-color); - height: 4em; - width: 4em; - padding: 1em; + height: 3.5em; + width: 3.5em; + padding: 0.75em; border-radius: 1em; } From e74b4e380498133c90ebda900b116730daa74f70 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 15 Nov 2020 01:16:35 +0100 Subject: [PATCH 06/20] Small fixes, perf improvements, remove duplicate images of ghost bikes, add wall mount --- InitUiElements.ts | 4 +-- Logic/FilteredLayer.ts | 16 ++++------ State.ts | 2 +- Svg.ts | 2 +- UI/FullScreenMessageBoxHandler.ts | 7 ++-- UI/Image/ImageUploadFlow.ts | 2 +- UI/Popup/FeatureInfoBox.ts | 1 - UI/Popup/TagRenderingAnswer.ts | 5 ++- UI/SimpleAddUI.ts | 2 +- UI/WelcomeMessage.ts | 3 +- assets/layers/ghost_bike/ghost_bike.json | 3 +- assets/svg/camera-plus.svg | 4 +-- .../surveillance_cameras.json | 32 +++++++++++++++++++ 13 files changed, 57 insertions(+), 26 deletions(-) diff --git a/InitUiElements.ts b/InitUiElements.ts index d6ba7b9..580153f 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -153,10 +153,10 @@ export class InitUiElements { * This is given to the div which renders fullscreen on mobile devices */ State.state.selectedElement.addCallback((feature) => { - if (feature?.feature?.properties === undefined) { + if (feature?.properties === undefined) { return; } - const data = feature.feature.properties; + const data = feature.properties; // Which is the applicable set? for (const layer of layoutToUse.layers) { if (typeof layer === "string") { diff --git a/Logic/FilteredLayer.ts b/Logic/FilteredLayer.ts index c90e441..5e0a106 100644 --- a/Logic/FilteredLayer.ts +++ b/Logic/FilteredLayer.ts @@ -322,14 +322,9 @@ export class FilteredLayer { eventSource.addCallback(updateStyle); function openPopup(e) { - updateStyle() - - if (feature.geometry.type === "Point") { - State.state.selectedElement.setData({feature: feature}); - return; // Points bind there own popups + return; // Points bind their own popups } - const uiElement = self._showOnPopup(eventSource, feature); L.popup({ autoPan: true, @@ -337,14 +332,17 @@ export class FilteredLayer { .setLatLng(e.latlng) .openOn(State.state.bm.map); uiElement.Update(); - State.state.selectedElement.setData({feature: feature}); - if (e) { L.DomEvent.stop(e); // Marks the event as consumed } } - layer.on("click", openPopup); + layer.on("click", (e) => { + updateStyle(); + openPopup(e); + State.state.selectedElement.setData(feature); + + }); } }); diff --git a/State.ts b/State.ts index f3da376..89aa915 100644 --- a/State.ts +++ b/State.ts @@ -82,7 +82,7 @@ export default class State { /** The latest element that was selected - used to generate the right UI at the right place */ - public readonly selectedElement = new UIEventSource<{ feature: any }>(undefined); + public readonly selectedElement = new UIEventSource(undefined); public readonly zoom: UIEventSource; public readonly lat: UIEventSource; diff --git a/Svg.ts b/Svg.ts index 1053b48..3041d22 100644 --- a/Svg.ts +++ b/Svg.ts @@ -34,7 +34,7 @@ export default class Svg { public static bug_svg() { return new FixedUiElement(Svg.bug);} public static bug_ui() { return new FixedUiElement(Svg.bug_img);} - public static camera_plus = " image/svg+xml " + public static camera_plus = " image/svg+xml " public static camera_plus_img = Img.AsImageElement(Svg.camera_plus) public static camera_plus_svg() { return new FixedUiElement(Svg.camera_plus);} public static camera_plus_ui() { return new FixedUiElement(Svg.camera_plus_img);} diff --git a/UI/FullScreenMessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts index fe757ca..c6b7a1f 100644 --- a/UI/FullScreenMessageBoxHandler.ts +++ b/UI/FullScreenMessageBoxHandler.ts @@ -13,9 +13,11 @@ export class FullScreenMessageBox extends UIElement { private readonly returnToTheMap: UIElement; constructor(onClear: (() => void)) { - super(State.state.fullScreenMessage); + super(); this.HideOnEmpty(true); + const self = this; State.state.fullScreenMessage.addCallbackAndRun(uiElement => { + self.Update(); if (uiElement === undefined) { location.hash = ""; } else { @@ -28,14 +30,12 @@ export class FullScreenMessageBox extends UIElement { window.onhashchange = function () { if (location.hash === "") { // No more element: back to the map! - console.log("Clearing full screen message"); State.state.fullScreenMessage.setData(undefined); onClear(); } } } - const self = this; this.returnToTheMap = new Combine([Translations.t.general.returnToTheMap.Clone().SetStyle("font-size:xx-large")]) .SetStyle("background:#7ebc6f;" + @@ -69,7 +69,6 @@ export class FullScreenMessageBox extends UIElement { } const el = document.getElementById(this.id); - console.warn(el, el.style.display); const uielement = new Combine([State.state.fullScreenMessage.data]).SetStyle( "display:block;" + diff --git a/UI/Image/ImageUploadFlow.ts b/UI/Image/ImageUploadFlow.ts index 6836771..aa75f7b 100644 --- a/UI/Image/ImageUploadFlow.ts +++ b/UI/Image/ImageUploadFlow.ts @@ -115,7 +115,7 @@ export class ImageUploadFlow extends UIElement { "cursor:pointer;" + "padding: 0.5em;" + "border-radius: 1em;" + - "border: 3px solid var(--popup-border);" + + "border: 3px solid var(--foreground-color);" + "box-sizing:border-box;") const actualInputElement = diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index c6f59b0..ed66eb2 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -40,7 +40,6 @@ export class FeatureInfoBox extends UIElement { } InnerRender(): string { - console.error("Inner rendering infobox for ", this._tags.data.id, this.id) return new Combine([ new Combine([this._title, this._titleIcons]) .SetClass("featureinfobox-titlebar"), diff --git a/UI/Popup/TagRenderingAnswer.ts b/UI/Popup/TagRenderingAnswer.ts index f560bf2..9bafc24 100644 --- a/UI/Popup/TagRenderingAnswer.ts +++ b/UI/Popup/TagRenderingAnswer.ts @@ -9,6 +9,7 @@ import {SubstitutedTranslation} from "../SpecialVisualizations"; export default class TagRenderingAnswer extends UIElement { private _tags: UIEventSource; private _configuration: TagRenderingConfig; + private _content: UIElement; constructor(tags: UIEventSource, configuration: TagRenderingConfig) { super(tags); @@ -31,7 +32,9 @@ export default class TagRenderingAnswer extends UIElement { if (tr === undefined) { return ""; } - return new SubstitutedTranslation(tr, this._tags).Render(); + // Bit of a hack; remember that the fields are updated + this._content = new SubstitutedTranslation(tr, this._tags); + return this._content.Render(); } } \ No newline at end of file diff --git a/UI/SimpleAddUI.ts b/UI/SimpleAddUI.ts index bed3179..c4acc5f 100644 --- a/UI/SimpleAddUI.ts +++ b/UI/SimpleAddUI.ts @@ -116,7 +116,7 @@ export class SimpleAddUI extends UIElement { const loc = State.state.bm.LastClickLocation.data; let feature = State.state.changes.createElement(tags, loc.lat, loc.lon); layerToAddTo.AddNewElement(feature); - State.state.selectedElement.setData({feature: feature}); + State.state.selectedElement.setData(feature); } } diff --git a/UI/WelcomeMessage.ts b/UI/WelcomeMessage.ts index 8fe2c00..4179285 100644 --- a/UI/WelcomeMessage.ts +++ b/UI/WelcomeMessage.ts @@ -24,9 +24,10 @@ export class WelcomeMessage extends UIElement { this.description = new Combine([ "

", layout.title, "

", layout.description - ]) layout.descriptionTail + + this.plzLogIn = diff --git a/assets/layers/ghost_bike/ghost_bike.json b/assets/layers/ghost_bike/ghost_bike.json index fce6998..d33d7df 100644 --- a/assets/layers/ghost_bike/ghost_bike.json +++ b/assets/layers/ghost_bike/ghost_bike.json @@ -43,8 +43,7 @@ } ], "tagRenderings": [ - "images", - { + { "render": { "en": "A ghost bike is a memorial for a cyclist who died in a traffic accident, in the form of a white bicycle placed permanently near the accident location.", "nl": "Een Witte Fiets (of Spookfiets) is een aandenken aan een fietser die bij een verkeersongeval om het leven kwam. Het gaat over een witgeschilderde fiets die geplaatst werd in de buurt van het ongeval.", diff --git a/assets/svg/camera-plus.svg b/assets/svg/camera-plus.svg index 77201f2..32f8c13 100644 --- a/assets/svg/camera-plus.svg +++ b/assets/svg/camera-plus.svg @@ -65,12 +65,12 @@ diff --git a/assets/themes/surveillance_cameras/surveillance_cameras.json b/assets/themes/surveillance_cameras/surveillance_cameras.json index c38e55e..994a827 100644 --- a/assets/themes/surveillance_cameras/surveillance_cameras.json +++ b/assets/themes/surveillance_cameras/surveillance_cameras.json @@ -230,6 +230,38 @@ } } ] + }, + { + "question": { + "en": "How is this camera placed?", + "nl": "Hoe is deze camera geplaatst?" + }, + "freeform": { + "key": "camera:mount" + }, + "mappings": [ + { + "if": "camera:mount=wall", + "then": { + "en": "This camera is placed against a wall", + "nl": "Deze camera hangt aan een muur" + } + }, + { + "if": "camera:mount=pole", + "then": { + "en": "This camera is placed one a pole", + "nl": "Deze camera staat op een paal" + } + }, + { + "if": "camera:mount=pole", + "then": { + "en": "This camera is placed one a pole", + "nl": "Deze camera staat op een paal" + } + } + ] } ], "hideUnderlayingFeaturesMinPercentage": 0, From 67bd817a38c7a5ac636b5ee1883bbd3ae719d927 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 15 Nov 2020 01:20:30 +0100 Subject: [PATCH 07/20] Css tweaks --- InitUiElements.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InitUiElements.ts b/InitUiElements.ts index 580153f..d978197 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -368,7 +368,7 @@ export class InitUiElements { return; } - layerControlPanel.SetStyle("display:block;padding:1em;border-radius:1em;"); + 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([ From 7ef2f429f2be5e54451bbc83976c467797af86c0 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 15 Nov 2020 03:10:44 +0100 Subject: [PATCH 08/20] Add fancy direction picker --- Svg.ts | 10 + UI/Input/Direction.ts | 39 ---- UI/Input/DirectionInput.ts | 92 ++++++++ UI/Input/ValidatedTextField.ts | 12 ++ UI/ShareScreen.ts | 24 ++- assets/svg/compass.svg | 199 ++++++++++++++++++ assets/svg/direction.svg | 31 +++ .../surveillance_cameras.json | 11 + index.css | 6 + package-lock.json | 6 +- test.ts | 9 +- 11 files changed, 386 insertions(+), 53 deletions(-) delete mode 100644 UI/Input/Direction.ts create mode 100644 UI/Input/DirectionInput.ts create mode 100644 assets/svg/compass.svg create mode 100644 assets/svg/direction.svg diff --git a/Svg.ts b/Svg.ts index 3041d22..4850a0c 100644 --- a/Svg.ts +++ b/Svg.ts @@ -49,6 +49,11 @@ export default class Svg { public static close_svg() { return new FixedUiElement(Svg.close);} public static close_ui() { return new FixedUiElement(Svg.close_img);} + public static compass = " image/svg+xml N S E W NW SW NE SE " + public static compass_img = Img.AsImageElement(Svg.compass) + public static compass_svg() { return new FixedUiElement(Svg.compass);} + public static compass_ui() { return new FixedUiElement(Svg.compass_img);} + public static crosshair_blue_center = " image/svg+xml " public static crosshair_blue_center_img = Img.AsImageElement(Svg.crosshair_blue_center) public static crosshair_blue_center_svg() { return new FixedUiElement(Svg.crosshair_blue_center);} @@ -69,6 +74,11 @@ 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 = " image/svg+xml " + 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 down = " image/svg+xml " public static down_img = Img.AsImageElement(Svg.down) public static down_svg() { return new FixedUiElement(Svg.down);} diff --git a/UI/Input/Direction.ts b/UI/Input/Direction.ts deleted file mode 100644 index d7dc5dc..0000000 --- a/UI/Input/Direction.ts +++ /dev/null @@ -1,39 +0,0 @@ -import {InputElement} from "./InputElement"; -import {UIEventSource} from "../../Logic/UIEventSource"; -import Combine from "../Base/Combine"; -import {FixedUiElement} from "../Base/FixedUiElement"; - -/** - * Selects a direction in degrees - */ -export default class Direction extends InputElement{ - - private readonly value: UIEventSource; - public readonly IsSelected: UIEventSource = new UIEventSource(false); - - constructor(value?: UIEventSource) { - super(); - this.value = value ?? new UIEventSource(undefined); - } - - - GetValue(): UIEventSource { - return this.value; - } - - InnerRender(): string { - return new Combine([ - new FixedUiElement("").SetStyle( - "position: absolute;top: calc(50% - 0.5em);left: calc(50% - 0.5em);width: 1em;height: 1em;background: red;border-radius: 1em"), - - ]) - .SetStyle("position:relative;display:block;width: min(100%, 25em); padding-top: min(100% , 25em); background:white; border: 1px solid black; border-radius: 999em") - .Render(); - } - - - IsValid(t: number): boolean { - return t >= 0 && t <= 360; - } - -} \ No newline at end of file diff --git a/UI/Input/DirectionInput.ts b/UI/Input/DirectionInput.ts new file mode 100644 index 0000000..7c6f152 --- /dev/null +++ b/UI/Input/DirectionInput.ts @@ -0,0 +1,92 @@ +import {InputElement} from "./InputElement"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import Combine from "../Base/Combine"; +import Svg from "../../Svg"; + +/** + * Selects a direction in degrees + */ +export default class DirectionInput extends InputElement { + + private readonly value: UIEventSource; + public readonly IsSelected: UIEventSource = new UIEventSource(false); + + constructor(value?: UIEventSource) { + super(); + this.dumbMode = false; + this.value = value ?? new UIEventSource(undefined); + + this.value.addCallbackAndRun(rotation => { + const selfElement = document.getElementById(this.id); + if (selfElement === null) { + return; + } + const cone = selfElement.getElementsByClassName("direction-svg")[0] as HTMLElement + cone.style.rotate = rotation + "deg"; + + }) + + } + + + GetValue(): UIEventSource { + return this.value; + } + + InnerRender(): string { + console.log("Inner render direction") + return new Combine([ + Svg.direction_svg().SetStyle( + `position: absolute;top: 0;left: 0;width: 100%;height: 100%;rotategs:${this.value.data}deg;`) + .SetClass("direction-svg"), + Svg.compass_svg().SetStyle( + "position: absolute;top: 0;left: 0;width: 100%;height: 100%;") + ]) + .SetStyle("position:relative;display:block;width: min(100%, 25em); padding-top: min(100% , 25em); background:white; border: 1px solid black; border-radius: 999em") + .Render(); + } + + protected InnerUpdate(htmlElement: HTMLElement) { + console.log("Inner update direction") + super.InnerUpdate(htmlElement); + const self = this; + + function onPosChange(x: number, y: number) { + const rect = htmlElement.getBoundingClientRect(); + const dx = -(rect.left + rect.right) / 2 + x; + const dy = (rect.top + rect.bottom) / 2 - y; + const angle = 180 * Math.atan2(dy, dx) / Math.PI; + const angleGeo = Math.floor((450 - angle) % 360); + self.value.setData(""+angleGeo) + } + + + htmlElement.ontouchmove = (ev: TouchEvent) => { + onPosChange(ev.touches[0].clientX, ev.touches[0].clientY); + } + + let isDown = false; + + htmlElement.onmousedown = (ev: MouseEvent) => { + isDown = true; + onPosChange(ev.x, ev.y); + ev.preventDefault(); + } + + htmlElement.onmouseup = (ev) => { + isDown = false; ev.preventDefault(); + } + + htmlElement.onmousemove = (ev: MouseEvent) => { + if (isDown) { + onPosChange(ev.x, ev.y); + } ev.preventDefault(); + } + } + + IsValid(str: string): boolean { + const t = Number(str); + return !isNaN(t) && t >= 0 && t <= 360; + } + +} \ No newline at end of file diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts index 2c89ef6..9ba1a18 100644 --- a/UI/Input/ValidatedTextField.ts +++ b/UI/Input/ValidatedTextField.ts @@ -9,6 +9,7 @@ import {UIEventSource} from "../../Logic/UIEventSource"; import CombinedInputElement from "./CombinedInputElement"; import SimpleDatePicker from "./SimpleDatePicker"; import OpeningHoursInput from "./OpeningHours/OpeningHoursInput"; +import DirectionInput from "./DirectionInput"; interface TextFieldDef { name: string, @@ -97,6 +98,17 @@ export default class ValidatedTextField { str = "" + str; return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) > 0 }), + ValidatedTextField.tp( + "direction", + "A geographical direction, in degrees. 0° is north, 90° is east", + (str) => { + str = "" + str; + return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) > 0 && Number(str) <= 360 + },str => str, + (value) => { + return new DirectionInput(value); + } + ), ValidatedTextField.tp( "float", "A decimal", diff --git a/UI/ShareScreen.ts b/UI/ShareScreen.ts index a682c6c..c9ea026 100644 --- a/UI/ShareScreen.ts +++ b/UI/ShareScreen.ts @@ -32,9 +32,17 @@ export class ShareScreen extends UIElement { const optionCheckboxes: UIElement[] = [] const optionParts: (UIEventSource)[] = []; + function check() { + return Svg.checkmark_svg().SetStyle("width: 1.5em; display:inline-block;"); + } + + function nocheck() { + return Svg.no_checkmark_svg().SetStyle("width: 1.5em; display: inline-block;"); + } + const includeLocation = new CheckBox( - new Combine([Svg.checkmark, tr.fsIncludeCurrentLocation]), - new Combine([Svg.no_checkmark, tr.fsIncludeCurrentLocation]), + new Combine([check(), tr.fsIncludeCurrentLocation]), + new Combine([nocheck(), tr.fsIncludeCurrentLocation]), true ) optionCheckboxes.push(includeLocation); @@ -68,8 +76,8 @@ export class ShareScreen extends UIElement { return tr.fsIncludeCurrentBackgroundMap.Subs({name: layer?.name ?? ""}).Render(); })); const includeCurrentBackground = new CheckBox( - new Combine([Svg.checkmark, currentBackground]), - new Combine([Svg.no_checkmark, currentBackground]), + new Combine([check(), currentBackground]), + new Combine([nocheck(), currentBackground]), true ) optionCheckboxes.push(includeCurrentBackground); @@ -83,8 +91,8 @@ export class ShareScreen extends UIElement { const includeLayerChoices = new CheckBox( - new Combine([Svg.checkmark, tr.fsIncludeCurrentLayers]), - new Combine([Svg.no_checkmark, tr.fsIncludeCurrentLayers]), + new Combine([check(), tr.fsIncludeCurrentLayers]), + new Combine([nocheck(), tr.fsIncludeCurrentLayers]), true ) optionCheckboxes.push(includeLayerChoices); @@ -113,8 +121,8 @@ export class ShareScreen extends UIElement { for (const swtch of switches) { const checkbox = new CheckBox( - new Combine([Svg.checkmark, Translations.W(swtch.human)]), - new Combine([Svg.no_checkmark, Translations.W(swtch.human)]), !swtch.reverse + new Combine([check(), Translations.W(swtch.human)]), + new Combine([nocheck(), Translations.W(swtch.human)]), !swtch.reverse ); optionCheckboxes.push(checkbox); optionParts.push(checkbox.isEnabled.map((isEn) => { diff --git a/assets/svg/compass.svg b/assets/svg/compass.svg new file mode 100644 index 0000000..52f0b97 --- /dev/null +++ b/assets/svg/compass.svg @@ -0,0 +1,199 @@ + + + + + + + image/svg+xml + + + + + + + N + S + E + W + NW + SW + NE + SE + diff --git a/assets/svg/direction.svg b/assets/svg/direction.svg new file mode 100644 index 0000000..e1eccac --- /dev/null +++ b/assets/svg/direction.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/assets/themes/surveillance_cameras/surveillance_cameras.json b/assets/themes/surveillance_cameras/surveillance_cameras.json index 994a827..29d9e27 100644 --- a/assets/themes/surveillance_cameras/surveillance_cameras.json +++ b/assets/themes/surveillance_cameras/surveillance_cameras.json @@ -96,6 +96,17 @@ } ] }, + { + "question": { + "en": "In which geographical direction does this camera film?", + "nl": "Naar welke geografische richting filmt deze camera?" + }, + "render": "Films to {camera:direction}", + "freeform": { + "key": "camera:direction", + "type": "direction" + } + }, { "freeform": { "key": "operator" diff --git a/index.css b/index.css index d5c4f87..ab483a4 100644 --- a/index.css +++ b/index.css @@ -26,6 +26,8 @@ a { svg { fill: var(--foreground-color) !important; stroke: var(--foreground-color) !important; + width: 100%; + height: 100%; } svg path { @@ -33,6 +35,10 @@ svg path { stroke: var(--foreground-color) !important; } +.direction-svg svg path{ + fill: var(--catch-detail-color) !important; +} + #leafletDiv { height: 100%; diff --git a/package-lock.json b/package-lock.json index b175d16..21245de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1774,9 +1774,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001066", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001066.tgz", - "integrity": "sha512-Gfj/WAastBtfxLws0RCh2sDbTK/8rJuSeZMecrSkNGYxPcv7EzblmDGfWQCFEQcSqYE2BRgQiJh8HOD07N5hIw==" + "version": "1.0.30001157", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001157.tgz", + "integrity": "sha512-gOerH9Wz2IRZ2ZPdMfBvyOi3cjaz4O4dgNwPGzx8EhqAs4+2IL/O+fJsbt+znSigujoZG8bVcIAUM/I/E5K3MA==" }, "canvas": { "version": "2.6.1", diff --git a/test.ts b/test.ts index f1b3381..e578eb8 100644 --- a/test.ts +++ b/test.ts @@ -1,10 +1,13 @@ //* -import Direction from "./UI/Input/Direction"; - -new Direction().AttachTo("maindiv") +import Direction from "./UI/Input/DirectionInput"; +import {UIEventSource} from "./Logic/UIEventSource"; +import {VariableUiElement} from "./UI/Base/VariableUIElement"; +const d = new UIEventSource(90); +new Direction(d).AttachTo("maindiv") +new VariableUiElement(d.map(d => ""+d+"°")).AttachTo("extradiv") /*/ From 1bd5411071b95df9e058d8ef13f7ddadde760fb3 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 15 Nov 2020 03:22:41 +0100 Subject: [PATCH 09/20] Download logo --- UI/Input/DirectionInput.ts | 2 +- assets/themes/surveillance_cameras/logo.svg | 8 ++++++++ .../themes/surveillance_cameras/surveillance_cameras.json | 4 ++-- 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 assets/themes/surveillance_cameras/logo.svg diff --git a/UI/Input/DirectionInput.ts b/UI/Input/DirectionInput.ts index 7c6f152..3d7f89a 100644 --- a/UI/Input/DirectionInput.ts +++ b/UI/Input/DirectionInput.ts @@ -37,7 +37,7 @@ export default class DirectionInput extends InputElement { console.log("Inner render direction") return new Combine([ Svg.direction_svg().SetStyle( - `position: absolute;top: 0;left: 0;width: 100%;height: 100%;rotategs:${this.value.data}deg;`) + `position: absolute;top: 0;left: 0;width: 100%;height: 100%;rotate:${this.value.data}deg;`) .SetClass("direction-svg"), Svg.compass_svg().SetStyle( "position: absolute;top: 0;left: 0;width: 100%;height: 100%;") diff --git a/assets/themes/surveillance_cameras/logo.svg b/assets/themes/surveillance_cameras/logo.svg new file mode 100644 index 0000000..3687143 --- /dev/null +++ b/assets/themes/surveillance_cameras/logo.svg @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/assets/themes/surveillance_cameras/surveillance_cameras.json b/assets/themes/surveillance_cameras/surveillance_cameras.json index 29d9e27..67bd8c5 100644 --- a/assets/themes/surveillance_cameras/surveillance_cameras.json +++ b/assets/themes/surveillance_cameras/surveillance_cameras.json @@ -17,7 +17,7 @@ "nl" ], "maintainer": "", - "icon": "https://upload.wikimedia.org/wikipedia/commons/b/b7/Video_surveillance_logo.svg", + "icon": "./assets/themes/surveillance_cameras/logo.svg", "version": "0", "startLat": 0, "startLon": 0, @@ -277,7 +277,7 @@ ], "hideUnderlayingFeaturesMinPercentage": 0, "icon": { - "render": "https://upload.wikimedia.org/wikipedia/commons/b/b7/Video_surveillance_logo.svg" + "render": "./assets/themes/surveillance_cameras/logo.svg" }, "width": { "render": "8" From e2881840da6c439c44adf2025cec1f42a9232add Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 15 Nov 2020 14:12:31 +0100 Subject: [PATCH 10/20] Fix touch direction input --- UI/Input/DirectionInput.ts | 2 ++ test.ts | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/UI/Input/DirectionInput.ts b/UI/Input/DirectionInput.ts index 3d7f89a..d6399fd 100644 --- a/UI/Input/DirectionInput.ts +++ b/UI/Input/DirectionInput.ts @@ -62,7 +62,9 @@ export default class DirectionInput extends InputElement { htmlElement.ontouchmove = (ev: TouchEvent) => { + console.log("Getting a touch", ev.touches[0].clientX, ev.touches[0].clientY) onPosChange(ev.touches[0].clientX, ev.touches[0].clientY); + ev.preventDefault(); } let isDown = false; diff --git a/test.ts b/test.ts index e578eb8..87cb971 100644 --- a/test.ts +++ b/test.ts @@ -5,9 +5,15 @@ import Direction from "./UI/Input/DirectionInput"; import {UIEventSource} from "./Logic/UIEventSource"; import {VariableUiElement} from "./UI/Base/VariableUIElement"; -const d = new UIEventSource(90); +const d = new UIEventSource("90"); new Direction(d).AttachTo("maindiv") -new VariableUiElement(d.map(d => ""+d+"°")).AttachTo("extradiv") +new VariableUiElement(d.map(d => "" + d + "°")).AttachTo("extradiv") + +UIEventSource.Chronic(25, () => { + const degr = (Number(d.data) + 1) % 360; + d.setData(""+ degr); + return true; +}) /*/ From e8d176cfe4d9b1a6610c10e22b3225efbd671ac4 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 15 Nov 2020 14:56:20 +0100 Subject: [PATCH 11/20] Create icons for surveillance cams --- State.ts | 2 +- Svg.ts | 2 +- UI/FullScreenMessageBoxHandler.ts | 7 +- assets/themes/surveillance_cameras/cam.svg | 61 +++++++++++++++ assets/themes/surveillance_cameras/dome.svg | 85 +++++++++++++++++++++ 5 files changed, 151 insertions(+), 6 deletions(-) create mode 100644 assets/themes/surveillance_cameras/cam.svg create mode 100644 assets/themes/surveillance_cameras/dome.svg diff --git a/State.ts b/State.ts index 89aa915..c4faf55 100644 --- a/State.ts +++ b/State.ts @@ -22,7 +22,7 @@ export default class State { // The singleton of the global state public static state: State; - public static vNumber = "0.1.3"; + public static vNumber = "0.1.3-rc1"; // The user journey states thresholds when a new feature gets unlocked public static userJourney = { diff --git a/Svg.ts b/Svg.ts index 4850a0c..f5a8d65 100644 --- a/Svg.ts +++ b/Svg.ts @@ -104,7 +104,7 @@ export default class Svg { public static help_svg() { return new FixedUiElement(Svg.help);} public static help_ui() { return new FixedUiElement(Svg.help_img);} - public static home = " " + public static home = " " public static home_img = Img.AsImageElement(Svg.home) public static home_svg() { return new FixedUiElement(Svg.home);} public static home_ui() { return new FixedUiElement(Svg.home_img);} diff --git a/UI/FullScreenMessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts index c6b7a1f..855eb62 100644 --- a/UI/FullScreenMessageBoxHandler.ts +++ b/UI/FullScreenMessageBoxHandler.ts @@ -11,6 +11,7 @@ export class FullScreenMessageBox extends UIElement { private static readonly _toTheMap_height : string = "5em"; private readonly returnToTheMap: UIElement; + private _content: UIElement; constructor(onClear: (() => void)) { super(); @@ -67,10 +68,8 @@ export class FullScreenMessageBox extends UIElement { if (State.state.fullScreenMessage.data === undefined) { return ""; } - - const el = document.getElementById(this.id); - - const uielement = new Combine([State.state.fullScreenMessage.data]).SetStyle( + this._content = State.state.fullScreenMessage.data; + const uielement = new Combine([this._content]).SetStyle( "display:block;" + "padding: 1em;" + "padding-bottom:6em;" + diff --git a/assets/themes/surveillance_cameras/cam.svg b/assets/themes/surveillance_cameras/cam.svg new file mode 100644 index 0000000..0fcce84 --- /dev/null +++ b/assets/themes/surveillance_cameras/cam.svg @@ -0,0 +1,61 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/assets/themes/surveillance_cameras/dome.svg b/assets/themes/surveillance_cameras/dome.svg new file mode 100644 index 0000000..28d0e32 --- /dev/null +++ b/assets/themes/surveillance_cameras/dome.svg @@ -0,0 +1,85 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + From a144cf17f4cfe30c1ef5b8fb1278e8b2ad8339fd Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 15 Nov 2020 16:08:17 +0100 Subject: [PATCH 12/20] Small tweaks --- InitUiElements.ts | 2 +- Svg.ts | 2 +- UI/Input/ValidatedTextField.ts | 2 +- assets/themes/surveillance_cameras/cam.svg | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/InitUiElements.ts b/InitUiElements.ts index d978197..7b660bf 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -290,7 +290,7 @@ export class InitUiElements { if (userdetails.csCount < State.userJourney.mapCompleteHelpUnlock) { return "" } - return Translations.t.general.aboutMapcomplete.Render(); + return new Combine([Translations.t.general.aboutMapcomplete, "
Version "+State.vNumber]).Render(); }, [Locale.language])) } ); diff --git a/Svg.ts b/Svg.ts index f5a8d65..4850a0c 100644 --- a/Svg.ts +++ b/Svg.ts @@ -104,7 +104,7 @@ export default class Svg { public static help_svg() { return new FixedUiElement(Svg.help);} public static help_ui() { return new FixedUiElement(Svg.help_img);} - public static home = " " + public static home = " " public static home_img = Img.AsImageElement(Svg.home) public static home_svg() { return new FixedUiElement(Svg.home);} public static home_ui() { return new FixedUiElement(Svg.home_img);} diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts index 9ba1a18..0071ab3 100644 --- a/UI/Input/ValidatedTextField.ts +++ b/UI/Input/ValidatedTextField.ts @@ -100,7 +100,7 @@ export default class ValidatedTextField { }), ValidatedTextField.tp( "direction", - "A geographical direction, in degrees. 0° is north, 90° is east", + "A geographical direction, in degrees. 0° is north, 90° is east, ... Will return a value between 0 (incl) and 360 (excl)", (str) => { str = "" + str; return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) > 0 && Number(str) <= 360 diff --git a/assets/themes/surveillance_cameras/cam.svg b/assets/themes/surveillance_cameras/cam.svg index 0fcce84..dbe448f 100644 --- a/assets/themes/surveillance_cameras/cam.svg +++ b/assets/themes/surveillance_cameras/cam.svg @@ -39,19 +39,19 @@ inkscape:window-height="1001" id="namedview8" showgrid="false" - inkscape:zoom="3.1466667" - inkscape:cx="77.828572" - inkscape:cy="166.9393" + inkscape:zoom="1.5733333" + inkscape:cx="-95.73727" + inkscape:cy="204.79067" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg6" /> + sodipodi:nodetypes="cccccccccccssccc" /> Date: Sun, 15 Nov 2020 19:17:36 +0100 Subject: [PATCH 13/20] Tweaks to icons, add direction to benches, tweak to direction input --- UI/FullScreenMessageBoxHandler.ts | 4 +- UI/Input/DirectionInput.ts | 6 +- assets/themes/benches/benches.json | 12 +++- assets/themes/surveillance_cameras/cam.svg | 18 ++--- assets/themes/surveillance_cameras/dome.svg | 24 +++---- assets/themes/surveillance_cameras/logo.svg | 69 ++++++++++++++++--- .../surveillance_cameras.json | 11 ++- 7 files changed, 103 insertions(+), 41 deletions(-) diff --git a/UI/FullScreenMessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts index 855eb62..b94595e 100644 --- a/UI/FullScreenMessageBoxHandler.ts +++ b/UI/FullScreenMessageBoxHandler.ts @@ -39,14 +39,14 @@ export class FullScreenMessageBox extends UIElement { this.returnToTheMap = new Combine([Translations.t.general.returnToTheMap.Clone().SetStyle("font-size:xx-large")]) - .SetStyle("background:#7ebc6f;" + + .SetStyle("background:var(--catch-detail-color);" + "position: fixed;" + "z-index: 10000;" + "bottom: 0;" + "left: 0;" + `height: ${FullScreenMessageBox._toTheMap_height};` + "width: 100vw;" + - "color: white;" + + "color: var(--catch-detail-color-contrast);" + "font-weight: bold;" + "pointer-events: all;" + "cursor: pointer;" + diff --git a/UI/Input/DirectionInput.ts b/UI/Input/DirectionInput.ts index d6399fd..f5c5c43 100644 --- a/UI/Input/DirectionInput.ts +++ b/UI/Input/DirectionInput.ts @@ -62,7 +62,11 @@ export default class DirectionInput extends InputElement { htmlElement.ontouchmove = (ev: TouchEvent) => { - console.log("Getting a touch", ev.touches[0].clientX, ev.touches[0].clientY) + onPosChange(ev.touches[0].clientX, ev.touches[0].clientY); + ev.preventDefault(); + } + + htmlElement.ontouchstart = (ev: TouchEvent) => { onPosChange(ev.touches[0].clientX, ev.touches[0].clientY); ev.preventDefault(); } diff --git a/assets/themes/benches/benches.json b/assets/themes/benches/benches.json index 8945da3..fef025e 100644 --- a/assets/themes/benches/benches.json +++ b/assets/themes/benches/benches.json @@ -190,6 +190,16 @@ "fr": "De quel matériau ce banc est-il fait ?" } }, + { + "question": { + "en": "In which direction are you looking when sitting on the bench?" + }, + "render": "When sitting on the bench, one looks towards {direction}*", + "freeform": { + "key": "direction", + "type": "direction" + } + }, { "render": { "en": "Colour: {colour}", @@ -297,7 +307,7 @@ "render": "8" }, "iconSize": { - "render": "20,20,center" + "render": "30,30,center" }, "color": { "render": "#00f" diff --git a/assets/themes/surveillance_cameras/cam.svg b/assets/themes/surveillance_cameras/cam.svg index dbe448f..d4f54e8 100644 --- a/assets/themes/surveillance_cameras/cam.svg +++ b/assets/themes/surveillance_cameras/cam.svg @@ -39,23 +39,17 @@ inkscape:window-height="1001" id="namedview8" showgrid="false" - inkscape:zoom="1.5733333" - inkscape:cx="-95.73727" - inkscape:cy="204.79067" + inkscape:zoom="4.4500586" + inkscape:cx="52.470633" + inkscape:cy="172.70315" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg6" /> - + style="fill:#000000;stroke:#ffffff;stroke-width:15;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:nodetypes="cccccccssccc" /> diff --git a/assets/themes/surveillance_cameras/dome.svg b/assets/themes/surveillance_cameras/dome.svg index 28d0e32..389759d 100644 --- a/assets/themes/surveillance_cameras/dome.svg +++ b/assets/themes/surveillance_cameras/dome.svg @@ -7,8 +7,8 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="292.05588" - height="159.1156" + width="300" + height="300" version="1.1" id="svg6" sodipodi:docname="dome.svg" @@ -40,33 +40,27 @@ id="namedview8" showgrid="false" inkscape:zoom="1.5733334" - inkscape:cx="37.492305" - inkscape:cy="-39.615653" + inkscape:cx="84.526201" + inkscape:cy="188.2981" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg6" /> - - - - - \ No newline at end of file + + + + + + image/svg+xml + + + + + + + + + diff --git a/assets/themes/surveillance_cameras/surveillance_cameras.json b/assets/themes/surveillance_cameras/surveillance_cameras.json index 67bd8c5..c502120 100644 --- a/assets/themes/surveillance_cameras/surveillance_cameras.json +++ b/assets/themes/surveillance_cameras/surveillance_cameras.json @@ -102,6 +102,7 @@ "nl": "Naar welke geografische richting filmt deze camera?" }, "render": "Films to {camera:direction}", + "condition": "camera:type!=dome", "freeform": { "key": "camera:direction", "type": "direction" @@ -277,13 +278,19 @@ ], "hideUnderlayingFeaturesMinPercentage": 0, "icon": { - "render": "./assets/themes/surveillance_cameras/logo.svg" + "render": "./assets/themes/surveillance_cameras/cam.svg", + "mappings": [ + { + "if": "camera:type=dome", + "then": "./assets/themes/surveillance_cameras/dome.svg" + } + ] }, "width": { "render": "8" }, "iconSize": { - "render": "30,30,center" + "render": "50,50,center" }, "color": { "render": "#00f" From 314894085a56300f0e33c9f2bacc13ee78ba3cd0 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 16 Nov 2020 01:59:30 +0100 Subject: [PATCH 14/20] Cleaning filtered layer --- Customizations/JSON/LayerConfig.ts | 59 ++++ InitUiElements.ts | 18 +- Logic/FilteredLayer.ts | 344 ++++++++------------ UI/FullScreenMessageBoxHandler.ts | 31 +- UI/Input/DirectionInput.ts | 1 - UI/Popup/EditableTagRendering.ts | 28 +- UI/Popup/TagRenderingQuestion.ts | 3 - UI/SimpleAddUI.ts | 3 +- assets/themes/surveillance_cameras/dome.svg | 10 +- css/mobile.css | 9 +- index.css | 4 +- package.json | 2 +- 12 files changed, 232 insertions(+), 280 deletions(-) diff --git a/Customizations/JSON/LayerConfig.ts b/Customizations/JSON/LayerConfig.ts index 7b610b1..268fa98 100644 --- a/Customizations/JSON/LayerConfig.ts +++ b/Customizations/JSON/LayerConfig.ts @@ -116,4 +116,63 @@ export default class LayerConfig { } + + + public GenerateLeafletStyle(tags: any): + { + color: string; + icon: { popupAnchor: [number, number]; iconAnchor: [number, number]; iconSize: [number, number]; iconUrl: string }; weight: number; dashArray: number[] + } { + const iconUrl = this.icon?.GetRenderValue(tags)?.txt; + const iconSize = (this.iconSize?.GetRenderValue(tags)?.txt ?? "40,40,center").split(","); + + + const dashArray = this.dashArray.GetRenderValue(tags)?.txt.split(" ").map(Number); + + function num(str, deflt = 40) { + const n = Number(str); + if (isNaN(n)) { + return deflt; + } + return n; + } + + const iconW = num(iconSize[0]); + const iconH = num(iconSize[1]); + const mode = iconSize[2] ?? "center" + + let anchorW = iconW / 2; + let anchorH = iconH / 2; + if (mode === "left") { + anchorW = 0; + } + if (mode === "right") { + anchorW = iconW; + } + + if (mode === "top") { + anchorH = 0; + } + if (mode === "bottom") { + anchorH = iconH; + } + + + const color = this.color?.GetRenderValue(tags)?.txt ?? "#00f"; + let weight = num(this.width?.GetRenderValue(tags)?.txt, 5); + return { + icon: + { + iconUrl: iconUrl, + iconSize: [iconW, iconH], + iconAnchor: [anchorW, anchorH], + popupAnchor: [0, 3 - anchorH] + }, + color: color, + weight: weight, + dashArray: dashArray + }; + } + + } \ No newline at end of file diff --git a/InitUiElements.ts b/InitUiElements.ts index 7b660bf..20af0c8 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -475,18 +475,16 @@ export class InitUiElements { throw "Layer " + layer + " was not substituted"; } - const generateInfo = (tagsES) => { - - return new FeatureInfoBox( - tagsES, - layer, - ) - }; - - const flayer: FilteredLayer = FilteredLayer.fromDefinition(layer, generateInfo); + const flayer: FilteredLayer = new FilteredLayer(layer, + (tagsES) => { + return new FeatureInfoBox( + tagsES, + layer, + ) + }); flayers.push(flayer); - QueryParameters.GetQueryParameter("layer-" + layer.id, "true", "Wehter or not layer "+layer.id+" is shown") + QueryParameters.GetQueryParameter("layer-" + layer.id, "true", "Wehter or not layer " + layer.id + " is shown") .map((str) => str !== "false", [], (b) => b.toString()) .syncWith( flayer.isDisplayed diff --git a/Logic/FilteredLayer.ts b/Logic/FilteredLayer.ts index 5e0a106..12522ab 100644 --- a/Logic/FilteredLayer.ts +++ b/Logic/FilteredLayer.ts @@ -25,13 +25,9 @@ export class FilteredLayer { public readonly layerDef: LayerConfig; private readonly _maxAllowedOverlap: number; - private readonly _style: (properties) => { color: string, weight?: number, icon: { iconUrl: string, iconSize?: [number, number], popupAnchor?: [number, number], iconAnchor?: [number, number] } }; - - /** The featurecollection from overpass */ private _dataFromOverpass: any[]; - private readonly _wayHandling: number; /** List of new elements, geojson features */ private _newElements = []; @@ -49,60 +45,7 @@ export class FilteredLayer { ) { this.layerDef = layerDef; - this._wayHandling = layerDef.wayHandling; this._showOnPopup = showOnPopup; - this._style = (tags) => { - - const iconUrl = layerDef.icon?.GetRenderValue(tags)?.txt; - const iconSize = (layerDef.iconSize?.GetRenderValue(tags)?.txt ?? "40,40,center").split(","); - - - const dashArray = layerDef.dashArray.GetRenderValue(tags)?.txt.split(" ").map(Number); - - function num(str, deflt = 40) { - const n = Number(str); - if (isNaN(n)) { - return deflt; - } - return n; - } - - const iconW = num(iconSize[0]); - const iconH = num(iconSize[1]); - const mode = iconSize[2] ?? "center" - - let anchorW = iconW / 2; - let anchorH = iconH / 2; - if (mode === "left") { - anchorW = 0; - } - if (mode === "right") { - anchorW = iconW; - } - - if (mode === "top") { - anchorH = 0; - } - if (mode === "bottom") { - anchorH = iconH; - } - - - const color = layerDef.color?.GetRenderValue(tags)?.txt ?? "#00f"; - let weight = num(layerDef.width?.GetRenderValue(tags)?.txt, 5); - return { - icon: - { - iconUrl: iconUrl, - iconSize: [iconW, iconH], - iconAnchor: [anchorW, anchorH], - popupAnchor: [0, 3 - anchorH] - }, - color: color, - weight: weight, - dashArray: dashArray - }; - }; this.name = name; this.filters = layerDef.overpassTags; this._maxAllowedOverlap = layerDef.hideUnderlayingFeaturesMinPercentage; @@ -123,13 +66,6 @@ export class FilteredLayer { } }) } - - static fromDefinition(definition, showOnPopup: (tags: UIEventSource, feature: any) => UIElement): FilteredLayer { - return new FilteredLayer(definition, showOnPopup); - - } - - /** * 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 @@ -138,32 +74,15 @@ export class FilteredLayer { const leftoverFeatures = []; const selfFeatures = []; for (let feature of geojson.features) { - // feature.properties contains all the properties - const tags = TagUtils.proprtiesToKV(feature.properties); - if (!this.filters.matches(tags)) { leftoverFeatures.push(feature); continue; } - - if (feature.geometry.type !== "Point") { - const centerPoint = GeoOperations.centerpoint(feature); - if (this._wayHandling === LayerConfig.WAYHANDLING_CENTER_AND_WAY) { - selfFeatures.push(centerPoint); - } else if (this._wayHandling === LayerConfig.WAYHANDLING_CENTER_ONLY) { - feature = centerPoint; - } - } selfFeatures.push(feature); - } - - this.RenderLayer({ - type: "FeatureCollection", - features: selfFeatures - }) + this.RenderLayer(selfFeatures) const notShadowed = []; for (const feature of leftoverFeatures) { @@ -186,18 +105,140 @@ export class FilteredLayer { public AddNewElement(element) { this._newElements.push(element); - this.RenderLayer({features: this._dataFromOverpass}, element); // Update the layer - + this.RenderLayer( this._dataFromOverpass); // Update the layer } - private RenderLayer(data, openPopupOf = undefined) { - let self = this; + private RenderLayer(features) { 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.bm.map.removeLayer(this._geolayer); } + // We fetch all the data we have to show: + let fusedFeatures = this.ApplyWayHandling(this.FuseData(features)); + console.log("Fused:",fusedFeatures) + + // And we copy some features as points - if needed + const data = { + type: "FeatureCollection", + features: fusedFeatures + } + + let self = this; + console.log(data); + this._geolayer = L.geoJSON(data, { + /* style: feature => { + self.layerDef.GenerateLeafletStyle(feature.properties); + return { + color: "#f00", + weight: 4 + } + },*/ + /* + pointToLayer: function (feature, latLng) { + // Point to layer converts the 'point' to a layer object - as the geojson layer natively cannot handle points + // Click handling is done in the next step + + const style = self.layerDef.GenerateLeafletStyle(feature.properties); + let marker; + if (style.icon === undefined) { + marker = L.circle(latLng, { + radius: 25, + color: style.color + }); + } else if (style.icon.iconUrl.startsWith("$circle")) { + marker = L.circle(latLng, { + radius: 25, + color: style.color + }); + } else { + if (style.icon.iconSize === undefined) { + style.icon.iconSize = [50, 50] + } + + marker = L.marker(latLng, { + icon: L.icon(style.icon) + }); + } + return marker; + },*/ +/* + onEachFeature: function (feature, layer:Layer) { + + layer.on("click", (e) => { + if (layer.getPopup() === undefined + && (window.screen.availHeight > 600 || window.screen.availWidth > 600) // We DON'T trigger this code on small screens! No need to create a popup + ) { + const popup = L.popup({ + autoPan: true, + closeOnEscapeKey: true, + }, layer); + + // @ts-ignore + popup.setLatLng(e.latlng) + + layer.bindPopup(popup); + const eventSource = State.state.allElements.addOrGetElement(feature); + const uiElement = self._showOnPopup(eventSource, feature); + // We first render the UIelement (which'll still need an update later on...) + // But at least it'll be visible already + popup.setContent(uiElement.Render()); + popup.openOn(State.state.bm.map); + // popup.openOn(State.state.bm.map); + // ANd we perform the pending update + uiElement.Update(); + } + // We set the element as selected... + State.state.selectedElement.setData(feature); + + // We mark the event as consumed + L.DomEvent.stop(e); + }); + } + */ + } + ) + ; + + if (this.combinedIsDisplayed.data) { + this._geolayer.addTo(State.state.bm.map); + } + + } + + private ApplyWayHandling(fusedFeatures: any[]) { + if (this.layerDef.wayHandling === LayerConfig.WAYHANDLING_DEFAULT) { + // We don't have to do anything special + return fusedFeatures; + } + + + // We have to convert all the ways into centerpoints + const existingPoints = []; + const newPoints = []; + const existingWays = []; + + for (const feature of fusedFeatures) { + if (feature.geometry.type === "Point") { + existingPoints.push(feature); + continue; + } + + existingWays.push(feature); + const centerPoint = GeoOperations.centerpoint(feature); + newPoints.push(centerPoint); + } + + fusedFeatures = existingPoints.concat(newPoints); + if (this.layerDef.wayHandling === LayerConfig.WAYHANDLING_CENTER_AND_WAY) { + fusedFeatures = fusedFeatures.concat(existingWays) + } + return fusedFeatures; + } + + //*Fuses the old and the new datasets*/ + private FuseData(data: any[]) { const oldData = this._dataFromOverpass ?? []; // We keep track of all the ids that are freshly loaded in order to avoid adding duplicates @@ -205,7 +246,7 @@ export class FilteredLayer { // A list of all the features to show const fusedFeatures = []; // First, we add all the fresh data: - for (const feature of data.features) { + for (const feature of data) { idsFromOverpass.add(feature.properties.id); fusedFeatures.push(feature); } @@ -226,133 +267,6 @@ export class FilteredLayer { fusedFeatures.push(feature); } } - - - // We use a new, fused dataset - data = { - type: "FeatureCollection", - features: fusedFeatures - } - - - // The data is split in two parts: the point and the rest - // The points get a special treatment in order to render them properly - // Note that some features might get a point representation as well - - const runWhenAdded: (() => void)[] = [] - - this._geolayer = L.geoJSON(data, { - style: function (feature) { - return self._style(feature.properties); - }, - pointToLayer: function (feature, latLng) { - const style = self._style(feature.properties); - let marker; - if (style.icon === undefined) { - marker = L.circle(latLng, { - radius: 25, - color: style.color - }); - - } else if (style.icon.iconUrl.startsWith("$circle")) { - marker = L.circle(latLng, { - radius: 25, - color: style.color - }); - } else { - if (style.icon.iconSize === undefined) { - style.icon.iconSize = [50, 50] - } - - // @ts-ignore - marker = L.marker(latLng, { - icon: L.icon(style.icon), - }); - } - let eventSource = State.state.allElements.addOrGetElement(feature); - const popup = L.popup({}, marker); - let uiElement: UIElement; - let content = undefined; - let p = marker.bindPopup(popup) - .on("popupopen", () => { - if (content === undefined) { - uiElement = self._showOnPopup(eventSource, feature); - // Lazily create the content - content = uiElement.Render(); - } - popup.setContent(content); - uiElement.Update(); - }); - - if (feature === openPopupOf) { - runWhenAdded.push(() => { - p.openPopup(); - }) - } - - return marker; - }, - - onEachFeature: function (feature, layer:Layer) { - - // We monky-patch the feature element with an update-style - function updateStyle () { - // @ts-ignore - if (layer.setIcon) { - const style = self._style(feature.properties); - const icon = style.icon; - if (icon.iconUrl) { - if (icon.iconUrl.startsWith("$circle")) { - // pass - } else { - // @ts-ignore - layer.setIcon(L.icon(icon)) - } - } - } else { - self._geolayer.setStyle(function (featureX) { - return self._style(featureX.properties); - }); - } - } - - let eventSource = State.state.allElements.addOrGetElement(feature); - - - eventSource.addCallback(updateStyle); - - function openPopup(e) { - if (feature.geometry.type === "Point") { - return; // Points bind their own popups - } - const uiElement = self._showOnPopup(eventSource, feature); - L.popup({ - autoPan: true, - }).setContent(uiElement.Render()) - .setLatLng(e.latlng) - .openOn(State.state.bm.map); - uiElement.Update(); - if (e) { - L.DomEvent.stop(e); // Marks the event as consumed - } - } - - layer.on("click", (e) => { - updateStyle(); - openPopup(e); - State.state.selectedElement.setData(feature); - - }); - } - }); - - if (this.combinedIsDisplayed.data) { - this._geolayer.addTo(State.state.bm.map); - for (const f of runWhenAdded) { - f(); - } - } + return fusedFeatures; } - - } \ No newline at end of file diff --git a/UI/FullScreenMessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts index b94595e..d1e9355 100644 --- a/UI/FullScreenMessageBoxHandler.ts +++ b/UI/FullScreenMessageBoxHandler.ts @@ -8,34 +8,13 @@ import Combine from "./Base/Combine"; */ export class FullScreenMessageBox extends UIElement { - private static readonly _toTheMap_height : string = "5em"; - private readonly returnToTheMap: UIElement; private _content: UIElement; constructor(onClear: (() => void)) { - super(); + super(State.state.fullScreenMessage); this.HideOnEmpty(true); const self = this; - State.state.fullScreenMessage.addCallbackAndRun(uiElement => { - self.Update(); - if (uiElement === undefined) { - location.hash = ""; - } else { - // The 'hash' makes sure a new piece of history is added. This makes the 'back-button' on android remove the popup - location.hash = "#element"; - } - }); - - if (window !== undefined) { - window.onhashchange = function () { - if (location.hash === "") { - // No more element: back to the map! - State.state.fullScreenMessage.setData(undefined); - onClear(); - } - } - } this.returnToTheMap = new Combine([Translations.t.general.returnToTheMap.Clone().SetStyle("font-size:xx-large")]) @@ -44,7 +23,7 @@ export class FullScreenMessageBox extends UIElement { "z-index: 10000;" + "bottom: 0;" + "left: 0;" + - `height: ${FullScreenMessageBox._toTheMap_height};` + + `height: var(--return-to-the-map-height);` + "width: 100vw;" + "color: var(--catch-detail-color-contrast);" + "font-weight: bold;" + @@ -55,10 +34,8 @@ export class FullScreenMessageBox extends UIElement { "padding-bottom: 1.2em;" + "box-sizing:border-box") .onClick(() => { - console.log("Returning...") State.state.fullScreenMessage.setData(undefined); onClear(); - self.Update(); }); } @@ -73,9 +50,9 @@ export class FullScreenMessageBox extends UIElement { "display:block;" + "padding: 1em;" + "padding-bottom:6em;" + - `margin-bottom:${FullScreenMessageBox._toTheMap_height};` + + `margin-bottom: var(--return-to-the-map-height);` + "box-sizing:border-box;" + - `height:calc(100vh - ${FullScreenMessageBox._toTheMap_height});` + + `height:calc(100vh - var(--return-to-the-map-height));` + "overflow-y: auto;" + "max-width:100vw;" + "overflow-x:hidden;" + diff --git a/UI/Input/DirectionInput.ts b/UI/Input/DirectionInput.ts index f5c5c43..7188ad2 100644 --- a/UI/Input/DirectionInput.ts +++ b/UI/Input/DirectionInput.ts @@ -68,7 +68,6 @@ export default class DirectionInput extends InputElement { htmlElement.ontouchstart = (ev: TouchEvent) => { onPosChange(ev.touches[0].clientX, ev.touches[0].clientY); - ev.preventDefault(); } let isDown = false; diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index fce78e6..b8e9d5b 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -27,24 +27,25 @@ export default class EditableTagRendering extends UIElement { this.ListenTo(this._editMode); this.ListenTo(State.state?.osmConnection?.userDetails) - const self = this; - this._answer = new TagRenderingAnswer(tags, configuration); - this._answer.SetStyle("width:100%;") if (this._configuration.question !== undefined) { - // 2.3em total width - if(State.state.featureSwitchUserbadge.data){ - - this._editButton = - Svg.pencil_svg().SetClass("edit-button") - .onClick(() => { - self._editMode.setData(true); - }); + if (State.state.featureSwitchUserbadge.data) { + // 2.3em total width + const self = this; + this._editButton = + Svg.pencil_svg().SetClass("edit-button") + .onClick(() => { + self._editMode.setData(true); + }); } + } + } - + private GenerateQuestion() { + const self = this; + if (this._configuration.question !== undefined) { // And at last, set up the skip button const cancelbutton = Translations.t.general.cancel.Clone() @@ -53,7 +54,7 @@ export default class EditableTagRendering extends UIElement { self._editMode.setData(false) }); - this._question = new TagRenderingQuestion(tags, configuration, + return new TagRenderingQuestion(this._tags, this._configuration, () => { self._editMode.setData(false) }, @@ -65,6 +66,7 @@ export default class EditableTagRendering extends UIElement { InnerRender(): string { if (this._editMode.data) { + this._question = this.GenerateQuestion(); return this._question.Render(); } diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index 10ba6bc..d803429 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -118,7 +118,6 @@ export default class TagRenderingQuestion extends UIElement { const inputEl = new InputElementMap( checkBoxes, (t0, t1) => { - console.log("IsEquiv?",t0, t1, t0?.isEquivalent(t1)) return t0?.isEquivalent(t1) ?? false }, (indices) => { @@ -162,8 +161,6 @@ export default class TagRenderingQuestion extends UIElement { } } - console.log(indices, freeformExtras); - if (freeformField) { if (freeformExtras.length > 0) { freeformField.GetValue().setData(new Tag(this._configuration.freeform.key, freeformExtras.join(";"))); diff --git a/UI/SimpleAddUI.ts b/UI/SimpleAddUI.ts index c4acc5f..db1c600 100644 --- a/UI/SimpleAddUI.ts +++ b/UI/SimpleAddUI.ts @@ -8,7 +8,6 @@ import Locale from "./i18n/Locale"; import State from "../State"; import {UIEventSource} from "../Logic/UIEventSource"; -import {Img} from "./Img"; import Svg from "../Svg"; /** @@ -115,8 +114,8 @@ export class SimpleAddUI extends UIElement { const loc = State.state.bm.LastClickLocation.data; let feature = State.state.changes.createElement(tags, loc.lat, loc.lon); - layerToAddTo.AddNewElement(feature); State.state.selectedElement.setData(feature); + layerToAddTo.AddNewElement(feature); } } diff --git a/assets/themes/surveillance_cameras/dome.svg b/assets/themes/surveillance_cameras/dome.svg index 389759d..ce0f5c0 100644 --- a/assets/themes/surveillance_cameras/dome.svg +++ b/assets/themes/surveillance_cameras/dome.svg @@ -40,27 +40,27 @@ id="namedview8" showgrid="false" inkscape:zoom="1.5733334" - inkscape:cx="84.526201" + inkscape:cx="-20.982269" inkscape:cy="188.2981" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg6" />