forked from MapComplete/MapComplete
		
	Fix bbox bug, add ids to filters, add filter state to the URL
This commit is contained in:
		
							parent
							
								
									38037014b0
								
							
						
					
					
						commit
						0a9e7c0b36
					
				
					 23 changed files with 248 additions and 59 deletions
				
			
		|  | @ -42,6 +42,8 @@ import {Tiles} from "./Models/TileRange"; | |||
| import {TileHierarchyAggregator} from "./UI/ShowDataLayer/PerTileCountAggregator"; | ||||
| import {BBox} from "./Logic/GeoOperations"; | ||||
| import StaticFeatureSource from "./Logic/FeatureSource/Sources/StaticFeatureSource"; | ||||
| import FilterConfig from "./Models/ThemeConfig/FilterConfig"; | ||||
| import FilteredLayer from "./Models/FilteredLayer"; | ||||
| 
 | ||||
| export class InitUiElements { | ||||
|     static InitAll( | ||||
|  | @ -406,8 +408,10 @@ export class InitUiElements { | |||
| 
 | ||||
|     private static InitLayers(): void { | ||||
|         const state = State.state; | ||||
|         const empty = [] | ||||
| 
 | ||||
|         state.filteredLayers = state.layoutToUse.map((layoutToUse) => { | ||||
|             const flayers = []; | ||||
|             const flayers: FilteredLayer[] = []; | ||||
| 
 | ||||
|             for (const layer of layoutToUse.layers) { | ||||
|                 const isDisplayed = QueryParameters.GetQueryParameter( | ||||
|  | @ -422,23 +426,40 @@ export class InitUiElements { | |||
|                 const flayer = { | ||||
|                     isDisplayed: isDisplayed, | ||||
|                     layerDef: layer, | ||||
|                     appliedFilters: new UIEventSource<TagsFilter>(undefined), | ||||
|                     appliedFilters: new UIEventSource<{ filter: FilterConfig, selected: number }[]>([]), | ||||
|                 }; | ||||
| 
 | ||||
|                 if (layer.filters.length > 0) { | ||||
|                     const filtersPerName = new Map<string, FilterConfig>() | ||||
|                     layer.filters.forEach(f => filtersPerName.set(f.id, f)) | ||||
|                     const qp = QueryParameters.GetQueryParameter("filter-" + layer.id, "","Filtering state for a layer") | ||||
|                     flayer.appliedFilters.map(filters => { | ||||
|                         filters = filters ?? [] | ||||
|                         return filters.map(f => f.filter.id + "." + f.selected).join(",") | ||||
|                     }, [], textual => { | ||||
|                         if(textual.length === 0){ | ||||
|                             return empty | ||||
|                         } | ||||
|                         return textual.split(",").map(part => { | ||||
|                             const [filterId, selected] = part.split("."); | ||||
|                             return {filter: filtersPerName.get(filterId), selected: Number(selected)} | ||||
|                         }).filter(f => f.filter !== undefined && !isNaN(f.selected)) | ||||
|                     }).syncWith(qp, true) | ||||
|                 } | ||||
| 
 | ||||
|                 flayers.push(flayer); | ||||
|             } | ||||
|             return flayers; | ||||
|         }); | ||||
| 
 | ||||
|         const layers = State.state.layoutToUse.data.layers | ||||
|         const clusterShow = Math.min(...layers.map(layer => layer.minzoom)) | ||||
| 
 | ||||
|         const layers = State.state.layoutToUse.data.layers | ||||
| 
 | ||||
|         const clusterCounter = TileHierarchyAggregator.createHierarchy() | ||||
|         new ShowDataLayer({ | ||||
|             features: clusterCounter.getCountsForZoom(State.state.locationControl, State.state.layoutToUse.data.clustering.minNeededElements), | ||||
|             leafletMap: State.state.leafletMap, | ||||
|             layerToShow: ShowTileInfo.styling, | ||||
|             doShowLayer: layers.length === 1 ? undefined : State.state.locationControl.map(l => l.zoom < clusterShow) | ||||
|         }) | ||||
| 
 | ||||
|         State.state.featurePipeline = new FeaturePipeline( | ||||
|  |  | |||
|  | @ -5,13 +5,14 @@ import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; | |||
| import Hash from "../../Web/Hash"; | ||||
| import {BBox} from "../../GeoOperations"; | ||||
| 
 | ||||
| export default class FilteringFeatureSource implements FeatureSourceForLayer , Tiled { | ||||
| export default class FilteringFeatureSource implements FeatureSourceForLayer, Tiled { | ||||
|     public features: UIEventSource<{ feature: any; freshness: Date }[]> = | ||||
|         new UIEventSource<{ feature: any; freshness: Date }[]>([]); | ||||
|     public readonly name; | ||||
|     public readonly layer: FilteredLayer; | ||||
| public readonly tileIndex : number | ||||
|     public readonly bbox : BBox | ||||
|     public readonly tileIndex: number | ||||
|     public readonly bbox: BBox | ||||
| 
 | ||||
|     constructor( | ||||
|         state: { | ||||
|             locationControl: UIEventSource<{ zoom: number }>, | ||||
|  | @ -21,7 +22,7 @@ public readonly tileIndex : number | |||
|         upstream: FeatureSourceForLayer | ||||
|     ) { | ||||
|         const self = this; | ||||
|         this.name = "FilteringFeatureSource("+upstream.name+")" | ||||
|         this.name = "FilteringFeatureSource(" + upstream.name + ")" | ||||
|         this.tileIndex = tileIndex | ||||
|         this.bbox = BBox.fromTileIndex(tileIndex) | ||||
| 
 | ||||
|  | @ -50,12 +51,15 @@ public readonly tileIndex : number | |||
|                 } | ||||
| 
 | ||||
|                 const tagsFilter = layer.appliedFilters.data; | ||||
|                 if (tagsFilter) { | ||||
|                     if (!tagsFilter.matchesProperties(f.feature.properties)) { | ||||
|                 for (const filter of tagsFilter ?? []) { | ||||
|                     const neededTags = filter.filter.options[filter.selected].osmTags | ||||
|                     if (!neededTags.matchesProperties(f.feature.properties)) { | ||||
|                         // Hidden by the filter on the layer itself - we want to hide it no matter wat
 | ||||
|                         return false; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
| 
 | ||||
|                 if (!layer.isDisplayed) { | ||||
|                     // The layer itself is either disabled or hidden due to zoom constraints
 | ||||
|                     // We should return true, but it might still match some other layer
 | ||||
|  | @ -80,7 +84,7 @@ public readonly tileIndex : number | |||
|         }); | ||||
| 
 | ||||
|         layer.appliedFilters.addCallback(_ => { | ||||
|             if(!layer.isDisplayed.data){ | ||||
|             if (!layer.isDisplayed.data) { | ||||
|                 // Currently not shown.
 | ||||
|                 // Note that a change in 'isSHown' will trigger an update as well, so we don't have to watch it another time
 | ||||
|                 return; | ||||
|  |  | |||
|  | @ -150,6 +150,7 @@ export default class MetaTagging { | |||
|                 for (const f of functions) { | ||||
|                     f(params, feature); | ||||
|                 } | ||||
|                  State.state.allElements.getEventSourceById(feature.properties.id).ping(); | ||||
|             } catch (e) { | ||||
|                 console.error("While calculating a tag value: ", e) | ||||
|             } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import {Utils} from "../Utils"; | |||
| 
 | ||||
| export default class Constants { | ||||
| 
 | ||||
|     public static vNumber = "0.10.0-alpha-2"; | ||||
|     public static vNumber = "0.10.0-alpha-3"; | ||||
|     public static ImgurApiKey = '7070e7167f0a25a' | ||||
| 
 | ||||
|     // The user journey states thresholds when a new feature gets unlocked
 | ||||
|  |  | |||
|  | @ -1,9 +1,10 @@ | |||
| import {UIEventSource} from "../Logic/UIEventSource"; | ||||
| import LayerConfig from "./ThemeConfig/LayerConfig"; | ||||
| import {And} from "../Logic/Tags/And"; | ||||
| import FilterConfig from "./ThemeConfig/FilterConfig"; | ||||
| 
 | ||||
| export default interface FilteredLayer { | ||||
|     readonly isDisplayed: UIEventSource<boolean>; | ||||
|     readonly appliedFilters: UIEventSource<And>; | ||||
|     readonly appliedFilters: UIEventSource<{filter: FilterConfig, selected: number}[]>; | ||||
|     readonly layerDef: LayerConfig; | ||||
| } | ||||
|  | @ -5,7 +5,8 @@ import Translations from "../../UI/i18n/Translations"; | |||
| import {TagUtils} from "../../Logic/Tags/TagUtils"; | ||||
| 
 | ||||
| export default class FilterConfig { | ||||
|     readonly options: { | ||||
|     public readonly id: string | ||||
|     public readonly options: { | ||||
|         question: Translation; | ||||
|         osmTags: TagsFilter; | ||||
|     }[]; | ||||
|  | @ -14,11 +15,18 @@ export default class FilterConfig { | |||
|         if (json.options === undefined) { | ||||
|             throw `A filter without options was given at ${context}` | ||||
|         } | ||||
|         if (json.id === undefined) { | ||||
|             throw `A filter without id was found at ${context}` | ||||
|         } | ||||
|         if(json.id.match(/^[a-zA-Z0-9_-]*$/) === null){ | ||||
|             throw `A filter with invalid id was found at ${context}. Ids should only contain letters, numbers or - _` | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         if (json.options.map === undefined) { | ||||
|             throw `A filter was given where the options aren't a list at ${context}` | ||||
|         } | ||||
| 
 | ||||
|         this.id = json.id; | ||||
|         this.options = json.options.map((option, i) => { | ||||
|             const question = Translations.T( | ||||
|                 option.question, | ||||
|  |  | |||
|  | @ -1,6 +1,10 @@ | |||
| import {AndOrTagConfigJson} from "./TagConfigJson"; | ||||
| 
 | ||||
| export default interface FilterConfigJson { | ||||
|     /** | ||||
|      * An id/name for this filter, used to set the URL parameters | ||||
|      */ | ||||
|     id: string, | ||||
|     /** | ||||
|      * The options for a filter | ||||
|      * If there are multiple options these will be a list of radio buttons | ||||
|  |  | |||
|  | @ -7,8 +7,6 @@ import Combine from "../Base/Combine"; | |||
| import Translations from "../i18n/Translations"; | ||||
| import {Translation} from "../i18n/Translation"; | ||||
| import Svg from "../../Svg"; | ||||
| import {TagsFilter} from "../../Logic/Tags/TagsFilter"; | ||||
| import {And} from "../../Logic/Tags/And"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import BaseUIElement from "../BaseUIElement"; | ||||
| import State from "../../State"; | ||||
|  | @ -16,11 +14,6 @@ import FilteredLayer from "../../Models/FilteredLayer"; | |||
| import BackgroundSelector from "./BackgroundSelector"; | ||||
| import FilterConfig from "../../Models/ThemeConfig/FilterConfig"; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Shows the filter | ||||
|  */ | ||||
| 
 | ||||
| export default class FilterView extends VariableUiElement { | ||||
|     constructor(filteredLayer: UIEventSource<FilteredLayer[]>) { | ||||
|         const backgroundSelector = new Toggle( | ||||
|  | @ -101,26 +94,52 @@ export default class FilterView extends VariableUiElement { | |||
|             return undefined; | ||||
|         } | ||||
| 
 | ||||
|         let listFilterElements: [BaseUIElement, UIEventSource<TagsFilter>][] = layer.filters.map( | ||||
|         const filterIndexes = new Map<string, number>() | ||||
|         layer.filters.forEach((f, i) => filterIndexes.set(f.id, i)) | ||||
| 
 | ||||
|         let listFilterElements: [BaseUIElement, UIEventSource<{ filter: FilterConfig, selected: number }>][] = layer.filters.map( | ||||
|             FilterView.createFilter | ||||
|         ); | ||||
| 
 | ||||
|         const update = () => { | ||||
|             let listTagsFilters = Utils.NoNull( | ||||
|                 listFilterElements.map((input) => input[1].data) | ||||
|             ); | ||||
|             flayer.appliedFilters.setData(new And(listTagsFilters)); | ||||
|         }; | ||||
|         listFilterElements.forEach((inputElement, i) => | ||||
|             inputElement[1].addCallback((changed) => { | ||||
|                 const oldValue = flayer.appliedFilters.data | ||||
|                  | ||||
|         listFilterElements.forEach((inputElement) => | ||||
|             inputElement[1].addCallback((_) => update()) | ||||
|                 if(changed === undefined){ | ||||
|                     // Lets figure out which filter should be removed
 | ||||
|                     // We know this inputElement corresponds with layer.filters[i]
 | ||||
|                     // SO, if there is a value in 'oldValue' with this filter, we have to recalculated
 | ||||
|                     if(!oldValue.some(f => f.filter === layer.filters[i])){ | ||||
|                         // The filter to remove is already gone, we can stop
 | ||||
|                         return; | ||||
|                     } | ||||
|                 }else if(oldValue.some(f => f.filter === changed.filter && f.selected === changed.selected)){ | ||||
|                     // The changed value is already there
 | ||||
|                     return; | ||||
|                 } | ||||
|                 const listTagsFilters = Utils.NoNull( | ||||
|                     listFilterElements.map((input) => input[1].data) | ||||
|                 ); | ||||
| 
 | ||||
|                 console.log(listTagsFilters, oldValue) | ||||
|                 flayer.appliedFilters.setData(listTagsFilters); | ||||
|             }) | ||||
|         ); | ||||
| 
 | ||||
|         flayer.appliedFilters.addCallbackAndRun(appliedFilters => { | ||||
|             if (appliedFilters === undefined || appliedFilters.and.length === 0) { | ||||
|                 listFilterElements.forEach(filter => filter[1].setData(undefined)) | ||||
|                 return | ||||
|             for (let i = 0; i < layer.filters.length; i++){ | ||||
|                 const filter = layer.filters[i]; | ||||
|                 let foundMatch = undefined | ||||
|                 for (const appliedFilter of appliedFilters) { | ||||
|                     if(appliedFilter.filter === filter){ | ||||
|                         foundMatch = appliedFilter | ||||
|                      break;    | ||||
|                     } | ||||
|                 } | ||||
|                  | ||||
|                 listFilterElements[i][1].setData(foundMatch) | ||||
|             } | ||||
| 
 | ||||
|         }) | ||||
| 
 | ||||
|         return new Combine(listFilterElements.map(input => input[0].SetClass("mt-3"))) | ||||
|  | @ -128,7 +147,7 @@ export default class FilterView extends VariableUiElement { | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private static createFilter(filterConfig: FilterConfig): [BaseUIElement, UIEventSource<TagsFilter>] { | ||||
|     private static createFilter(filterConfig: FilterConfig): [BaseUIElement, UIEventSource<{ filter: FilterConfig, selected: number }>] { | ||||
|         if (filterConfig.options.length === 1) { | ||||
|             let option = filterConfig.options[0]; | ||||
| 
 | ||||
|  | @ -142,20 +161,36 @@ export default class FilterView extends VariableUiElement { | |||
|                 .ToggleOnClick() | ||||
|                 .SetClass("block m-1") | ||||
| 
 | ||||
|             return [toggle, toggle.isEnabled.map(enabled => enabled ? option.osmTags : undefined, [], tags => tags !== undefined)] | ||||
|             const selected = { | ||||
|                 filter: filterConfig, | ||||
|                 selected: 0 | ||||
|             } | ||||
|             return [toggle, toggle.isEnabled.map(enabled => enabled ? selected : undefined, [], | ||||
|                 f => f?.filter === filterConfig && f?.selected === 0) | ||||
|             ] | ||||
|         } | ||||
| 
 | ||||
|         let options = filterConfig.options; | ||||
| 
 | ||||
|         const values = options.map((f, i) => ({ | ||||
|            filter: filterConfig, selected: i  | ||||
|         })) | ||||
|         const radio = new RadioButton( | ||||
|             options.map( | ||||
|                 (option) => | ||||
|                     new FixedInputElement(option.question.Clone(), option.osmTags) | ||||
|                 (option, i) => | ||||
|                     new FixedInputElement(option.question.Clone(), i) | ||||
|             ), | ||||
|             { | ||||
|                 dontStyle: true | ||||
|             } | ||||
|         ); | ||||
|         return [radio, radio.GetValue()] | ||||
|         return [radio,  | ||||
|             radio.GetValue().map( | ||||
|             i => values[i],  | ||||
|                 [], | ||||
|             selected => { | ||||
|                 return selected?.selected | ||||
|             } | ||||
|         )] | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -223,14 +223,14 @@ export default class SimpleAddUI extends Toggle { | |||
|                 ] | ||||
|             ).SetClass("flex flex-col") | ||||
|         ).onClick(() => { | ||||
|             preset.layerToAddTo.appliedFilters.setData(new And([])) | ||||
|             preset.layerToAddTo.appliedFilters.setData([]) | ||||
|             cancel() | ||||
|         }) | ||||
| 
 | ||||
|         const disableFiltersOrConfirm = new Toggle( | ||||
|             openLayerOrConfirm, | ||||
|             disableFilter, | ||||
|             preset.layerToAddTo.appliedFilters.map(filters => filters === undefined || filters.normalize().and.length === 0) | ||||
|             preset.layerToAddTo.appliedFilters.map(filters => filters === undefined || filters.length === 0) | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -258,6 +258,7 @@ | |||
|     "wayHandling": 1, | ||||
|     "filter": [ | ||||
|         { | ||||
|             "id": "wheelchair", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": { | ||||
|  | @ -275,6 +276,7 @@ | |||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "id": "shelter", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": { | ||||
|  |  | |||
|  | @ -170,6 +170,7 @@ | |||
|     ], | ||||
|     "filter": [ | ||||
|         { | ||||
|             "id": "opened-now", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": { | ||||
|  |  | |||
|  | @ -2627,6 +2627,7 @@ | |||
|     "wayHandling": 1, | ||||
|     "filter": [ | ||||
|         { | ||||
|             "id": "vehicle-type", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": { | ||||
|  | @ -2656,6 +2657,7 @@ | |||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "id": "working", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": { | ||||
|  | @ -2671,6 +2673,7 @@ | |||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "id": "connection_type", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": { | ||||
|  |  | |||
|  | @ -648,6 +648,7 @@ | |||
|   "wayHandling": 1, | ||||
|   "filter": [ | ||||
|     { | ||||
|       "id": "vehicle-type", | ||||
|       "options": [ | ||||
|         { | ||||
|           "question": { | ||||
|  | @ -677,6 +678,7 @@ | |||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "working", | ||||
|       "options": [ | ||||
|         { | ||||
|           "question": { | ||||
|  |  | |||
|  | @ -242,6 +242,7 @@ function run(file, protojson) { | |||
|     }) | ||||
|      | ||||
|     proto["filter"].push({ | ||||
|         id:"connection_type", | ||||
|         options: filterOptions | ||||
|     }) | ||||
| 
 | ||||
|  |  | |||
|  | @ -560,6 +560,7 @@ | |||
|     ], | ||||
|     "filter": [ | ||||
|         { | ||||
|             "id": "opened-now", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": { | ||||
|  | @ -571,6 +572,7 @@ | |||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "id": "vegetarian", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": { | ||||
|  | @ -589,6 +591,7 @@ | |||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "id": "vegan", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": { | ||||
|  | @ -605,6 +608,7 @@ | |||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "id": "halal", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": { | ||||
|  |  | |||
|  | @ -423,6 +423,7 @@ | |||
|     ], | ||||
|     "filter": [ | ||||
|         { | ||||
|             "id": "access", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": { | ||||
|  | @ -433,6 +434,7 @@ | |||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "id": "dogs", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": { | ||||
|  |  | |||
|  | @ -444,6 +444,7 @@ | |||
|     }, | ||||
|     "filter": [ | ||||
|         { | ||||
|             "id": "kid-books", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": "Kinderboeken aanwezig?", | ||||
|  | @ -452,6 +453,7 @@ | |||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "id": "adult-books", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": "Boeken voor volwassenen aanwezig?", | ||||
|  | @ -460,6 +462,7 @@ | |||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "id": "inside", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": "Binnen of buiten", | ||||
|  |  | |||
|  | @ -411,6 +411,7 @@ | |||
|     ], | ||||
|     "filter": [ | ||||
|         { | ||||
|             "id": "wheelchair", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": { | ||||
|  | @ -421,6 +422,7 @@ | |||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "id": "changing_table", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": { | ||||
|  | @ -431,6 +433,7 @@ | |||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "id": "free", | ||||
|             "options": [ | ||||
|                 { | ||||
|                     "question": { | ||||
|  |  | |||
|  | @ -143,6 +143,7 @@ | |||
|       }, | ||||
|       "filter": [ | ||||
|         { | ||||
|           "id": "name-alt", | ||||
|           "options": [ | ||||
|             { | ||||
|               "question": "Name contains 'alt'", | ||||
|  | @ -151,6 +152,7 @@ | |||
|           ] | ||||
|         }, | ||||
|         { | ||||
|           "id": "name-wenslijn", | ||||
|           "options": [ | ||||
|             { | ||||
|               "question": "Name contains 'wenslijn'", | ||||
|  | @ -159,6 +161,7 @@ | |||
|           ] | ||||
|         }, | ||||
|         { | ||||
|           "id": "name-omleiding", | ||||
|           "options": [ | ||||
|             { | ||||
|               "question": "Name contains 'omleiding'", | ||||
|  | @ -167,6 +170,7 @@ | |||
|           ] | ||||
|         }, | ||||
|         { | ||||
|           "id":"ref-alt", | ||||
|           "options": [ | ||||
|             { | ||||
|               "question": "Reference contains 'alt'", | ||||
|  | @ -175,6 +179,7 @@ | |||
|           ] | ||||
|         }, | ||||
|         { | ||||
|           "id": "missing_link", | ||||
|           "options": [ | ||||
|             { | ||||
|               "question": "No filter" | ||||
|  | @ -194,6 +199,7 @@ | |||
|           ] | ||||
|         }, | ||||
|         { | ||||
|           "id": "proposed", | ||||
|           "options": [ | ||||
|             { | ||||
|               "question": "No filter" | ||||
|  |  | |||
							
								
								
									
										60
									
								
								assets/themes/uk_addresses/housenumber_unknown_small.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								assets/themes/uk_addresses/housenumber_unknown_small.svg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | |||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    version="1.1" | ||||
|    viewBox="0 0 87.992996 87.883003" | ||||
|    id="svg12" | ||||
|    sodipodi:docname="housenumber_unknown_small.svg" | ||||
|    inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" | ||||
|    width="87.992996" | ||||
|    height="87.883003"> | ||||
|   <metadata | ||||
|      id="metadata18"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <defs | ||||
|      id="defs16" /> | ||||
|   <sodipodi:namedview | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1" | ||||
|      objecttolerance="10" | ||||
|      gridtolerance="10" | ||||
|      guidetolerance="10" | ||||
|      inkscape:pageopacity="0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:window-width="1920" | ||||
|      inkscape:window-height="1043" | ||||
|      id="namedview14" | ||||
|      showgrid="false" | ||||
|      inkscape:zoom="7.375" | ||||
|      inkscape:cx="-1.3561062" | ||||
|      inkscape:cy="19.621117" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="0" | ||||
|      inkscape:window-maximized="1" | ||||
|      inkscape:current-layer="svg12" /> | ||||
|   <path | ||||
|      d="m 42.372766,41.559418 h 3.247468 c 0.421762,0 0.76133,0.339541 0.76133,0.761329 v 3.241506 c 0,0.421761 -0.339541,0.761329 -0.76133,0.761329 h -3.247468 c -0.421762,0 -0.76133,-0.339541 -0.76133,-0.761329 v -3.241506 c 0,-0.421761 0.339541,-0.761329 0.76133,-0.761329 z" | ||||
|      style="fill:#495aad;stroke-width:0.0542103;paint-order:normal" | ||||
|      id="path6" | ||||
|      inkscape:connector-curvature="0" /> | ||||
|   <path | ||||
|      d="m 42.085614,42.793949 v 2.289464 c 0.381581,0 0.763173,0.381581 0.763173,0.763173 h 2.289463 c 0,-0.381581 0.381581,-0.763173 0.763173,-0.763173 v -2.289464 c -0.381581,0 -0.763173,-0.381581 -0.763173,-0.763172 h -2.289463 c 0,0.38158 -0.381581,0.763172 -0.763173,0.763172 z" | ||||
|      id="path8" | ||||
|      inkscape:connector-curvature="0" | ||||
|      style="fill:none;stroke:#ffffff;stroke-width:0.27187553" /> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 2.3 KiB | 
|  | @ -39,5 +39,13 @@ | |||
|       "https://github.com/streetcomplete/StreetComplete/tree/master/res/graphics", | ||||
|       "https://f-droid.org/packages/de.westnordost.streetcomplete/" | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     "path": "housenumber_unknown_small.svg", | ||||
|     "license": "CC0", | ||||
|     "authors": [ | ||||
|       "Pieter Vander Vennet" | ||||
|     ], | ||||
|     "sources": [] | ||||
|   } | ||||
| ] | ||||
|  | @ -15,11 +15,15 @@ | |||
|   "maintainer": "Pieter Vander Vennet, Rob Nickerson, Russ Garrett", | ||||
|   "icon": "./assets/themes/uk_addresses/housenumber_unknown.svg", | ||||
|   "version": "2021-09-17", | ||||
|   "startLat": -0.08528530407, | ||||
|   "startLon": 51.52103754846, | ||||
|   "startZoom": 18, | ||||
|   "widenFactor": 1.5, | ||||
|   "startLat": -0.08706, | ||||
|   "startLon": 51.52224, | ||||
|   "startZoom": 17, | ||||
|   "widenFactor": 1.01, | ||||
|   "socialImage": "", | ||||
|   "clustering": { | ||||
|     "minNeededFeatures": 25, | ||||
|     "maxZoom": 17 | ||||
|   }, | ||||
|   "layers": [ | ||||
|     { | ||||
|       "id": "to_import", | ||||
|  | @ -34,21 +38,21 @@ | |||
|       "minzoom": 12, | ||||
|       "wayHandling": 1, | ||||
|       "icon": { | ||||
|         "render": "./assets/themes/uk_addresses/housenumber_unknown.svg" | ||||
|       }, | ||||
|       "iconSize": { | ||||
|         "render": "40,40,center", | ||||
|         "render": "./assets/themes/uk_addresses/housenumber_unknown.svg", | ||||
|         "mappings": [ | ||||
|           { | ||||
|             "if": "_embedding_object:id~*", | ||||
|             "then": "15,15,center" | ||||
|             "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" | ||||
|           }, | ||||
|           { | ||||
|             "if": "_imported=yes", | ||||
|             "then": "8,8,center" | ||||
|             "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" | ||||
|           } | ||||
|         ] | ||||
|       }, | ||||
|       "iconSize": { | ||||
|         "render": "40,40,center" | ||||
|       }, | ||||
|       "title": { | ||||
|         "render": "Address to be determined" | ||||
|       }, | ||||
|  | @ -73,6 +77,22 @@ | |||
|         "_embedding_object:addr:housenumber=JSON.parse(feat.properties._embedding_object)?.['addr:housenumber']", | ||||
|         "_embedding_object:addr:street=JSON.parse(feat.properties._embedding_object)?.['addr:street']", | ||||
|         "_embedding_object:id=JSON.parse(feat.properties._embedding_object)?.id" | ||||
|       ], | ||||
|       "filter": [ | ||||
|         { | ||||
|           "id": "to_handle", | ||||
|           "options": [ | ||||
|             { | ||||
|               "question": "Only show non-matched objects", | ||||
|               "osmTags": { | ||||
|                 "and": [ | ||||
|                   "_imported=", | ||||
|                   "_embedding_object:id=" | ||||
|                 ] | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|  |  | |||
|  | @ -181,10 +181,10 @@ export default class GeoOperationsSpec extends T { | |||
|                 ["bbox bounds test", | ||||
|                     () => { | ||||
|                         const bbox = BBox.fromTile(16, 32754, 21785) | ||||
|                         equal(-0.0714111328125, bbox.minLon) | ||||
|                         equal(-0.076904296875, bbox.maxLon) | ||||
|                         equal(51.53266860674158, bbox.minLat) | ||||
|                         equal(51.5292513551899, bbox.maxLat) | ||||
|                         equal(-0.076904296875, bbox.minLon) | ||||
|                         equal(-0.0714111328125, bbox.maxLon) | ||||
|                         equal(51.5292513551899, bbox.minLat) | ||||
|                         equal(51.53266860674158, bbox.maxLat) | ||||
|                     } | ||||
|                 ] | ||||
|             ] | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue