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 {TileHierarchyAggregator} from "./UI/ShowDataLayer/PerTileCountAggregator";
|
||||||
import {BBox} from "./Logic/GeoOperations";
|
import {BBox} from "./Logic/GeoOperations";
|
||||||
import StaticFeatureSource from "./Logic/FeatureSource/Sources/StaticFeatureSource";
|
import StaticFeatureSource from "./Logic/FeatureSource/Sources/StaticFeatureSource";
|
||||||
|
import FilterConfig from "./Models/ThemeConfig/FilterConfig";
|
||||||
|
import FilteredLayer from "./Models/FilteredLayer";
|
||||||
|
|
||||||
export class InitUiElements {
|
export class InitUiElements {
|
||||||
static InitAll(
|
static InitAll(
|
||||||
|
@ -406,8 +408,10 @@ export class InitUiElements {
|
||||||
|
|
||||||
private static InitLayers(): void {
|
private static InitLayers(): void {
|
||||||
const state = State.state;
|
const state = State.state;
|
||||||
|
const empty = []
|
||||||
|
|
||||||
state.filteredLayers = state.layoutToUse.map((layoutToUse) => {
|
state.filteredLayers = state.layoutToUse.map((layoutToUse) => {
|
||||||
const flayers = [];
|
const flayers: FilteredLayer[] = [];
|
||||||
|
|
||||||
for (const layer of layoutToUse.layers) {
|
for (const layer of layoutToUse.layers) {
|
||||||
const isDisplayed = QueryParameters.GetQueryParameter(
|
const isDisplayed = QueryParameters.GetQueryParameter(
|
||||||
|
@ -422,23 +426,40 @@ export class InitUiElements {
|
||||||
const flayer = {
|
const flayer = {
|
||||||
isDisplayed: isDisplayed,
|
isDisplayed: isDisplayed,
|
||||||
layerDef: layer,
|
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);
|
flayers.push(flayer);
|
||||||
}
|
}
|
||||||
return flayers;
|
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()
|
const clusterCounter = TileHierarchyAggregator.createHierarchy()
|
||||||
new ShowDataLayer({
|
new ShowDataLayer({
|
||||||
features: clusterCounter.getCountsForZoom(State.state.locationControl, State.state.layoutToUse.data.clustering.minNeededElements),
|
features: clusterCounter.getCountsForZoom(State.state.locationControl, State.state.layoutToUse.data.clustering.minNeededElements),
|
||||||
leafletMap: State.state.leafletMap,
|
leafletMap: State.state.leafletMap,
|
||||||
layerToShow: ShowTileInfo.styling,
|
layerToShow: ShowTileInfo.styling,
|
||||||
doShowLayer: layers.length === 1 ? undefined : State.state.locationControl.map(l => l.zoom < clusterShow)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
State.state.featurePipeline = new FeaturePipeline(
|
State.state.featurePipeline = new FeaturePipeline(
|
||||||
|
|
|
@ -5,13 +5,14 @@ import {FeatureSourceForLayer, Tiled} from "../FeatureSource";
|
||||||
import Hash from "../../Web/Hash";
|
import Hash from "../../Web/Hash";
|
||||||
import {BBox} from "../../GeoOperations";
|
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 }[]> =
|
public features: UIEventSource<{ feature: any; freshness: Date }[]> =
|
||||||
new UIEventSource<{ feature: any; freshness: Date }[]>([]);
|
new UIEventSource<{ feature: any; freshness: Date }[]>([]);
|
||||||
public readonly name;
|
public readonly name;
|
||||||
public readonly layer: FilteredLayer;
|
public readonly layer: FilteredLayer;
|
||||||
public readonly tileIndex : number
|
public readonly tileIndex: number
|
||||||
public readonly bbox : BBox
|
public readonly bbox: BBox
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
state: {
|
state: {
|
||||||
locationControl: UIEventSource<{ zoom: number }>,
|
locationControl: UIEventSource<{ zoom: number }>,
|
||||||
|
@ -21,7 +22,7 @@ public readonly tileIndex : number
|
||||||
upstream: FeatureSourceForLayer
|
upstream: FeatureSourceForLayer
|
||||||
) {
|
) {
|
||||||
const self = this;
|
const self = this;
|
||||||
this.name = "FilteringFeatureSource("+upstream.name+")"
|
this.name = "FilteringFeatureSource(" + upstream.name + ")"
|
||||||
this.tileIndex = tileIndex
|
this.tileIndex = tileIndex
|
||||||
this.bbox = BBox.fromTileIndex(tileIndex)
|
this.bbox = BBox.fromTileIndex(tileIndex)
|
||||||
|
|
||||||
|
@ -50,12 +51,15 @@ public readonly tileIndex : number
|
||||||
}
|
}
|
||||||
|
|
||||||
const tagsFilter = layer.appliedFilters.data;
|
const tagsFilter = layer.appliedFilters.data;
|
||||||
if (tagsFilter) {
|
for (const filter of tagsFilter ?? []) {
|
||||||
if (!tagsFilter.matchesProperties(f.feature.properties)) {
|
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
|
// Hidden by the filter on the layer itself - we want to hide it no matter wat
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!layer.isDisplayed) {
|
if (!layer.isDisplayed) {
|
||||||
// The layer itself is either disabled or hidden due to zoom constraints
|
// The layer itself is either disabled or hidden due to zoom constraints
|
||||||
// We should return true, but it might still match some other layer
|
// We should return true, but it might still match some other layer
|
||||||
|
@ -80,7 +84,7 @@ public readonly tileIndex : number
|
||||||
});
|
});
|
||||||
|
|
||||||
layer.appliedFilters.addCallback(_ => {
|
layer.appliedFilters.addCallback(_ => {
|
||||||
if(!layer.isDisplayed.data){
|
if (!layer.isDisplayed.data) {
|
||||||
// Currently not shown.
|
// 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
|
// Note that a change in 'isSHown' will trigger an update as well, so we don't have to watch it another time
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -150,6 +150,7 @@ export default class MetaTagging {
|
||||||
for (const f of functions) {
|
for (const f of functions) {
|
||||||
f(params, feature);
|
f(params, feature);
|
||||||
}
|
}
|
||||||
|
State.state.allElements.getEventSourceById(feature.properties.id).ping();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("While calculating a tag value: ", e)
|
console.error("While calculating a tag value: ", e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import {Utils} from "../Utils";
|
||||||
|
|
||||||
export default class Constants {
|
export default class Constants {
|
||||||
|
|
||||||
public static vNumber = "0.10.0-alpha-2";
|
public static vNumber = "0.10.0-alpha-3";
|
||||||
public static ImgurApiKey = '7070e7167f0a25a'
|
public static ImgurApiKey = '7070e7167f0a25a'
|
||||||
|
|
||||||
// The user journey states thresholds when a new feature gets unlocked
|
// The user journey states thresholds when a new feature gets unlocked
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import {UIEventSource} from "../Logic/UIEventSource";
|
import {UIEventSource} from "../Logic/UIEventSource";
|
||||||
import LayerConfig from "./ThemeConfig/LayerConfig";
|
import LayerConfig from "./ThemeConfig/LayerConfig";
|
||||||
import {And} from "../Logic/Tags/And";
|
import {And} from "../Logic/Tags/And";
|
||||||
|
import FilterConfig from "./ThemeConfig/FilterConfig";
|
||||||
|
|
||||||
export default interface FilteredLayer {
|
export default interface FilteredLayer {
|
||||||
readonly isDisplayed: UIEventSource<boolean>;
|
readonly isDisplayed: UIEventSource<boolean>;
|
||||||
readonly appliedFilters: UIEventSource<And>;
|
readonly appliedFilters: UIEventSource<{filter: FilterConfig, selected: number}[]>;
|
||||||
readonly layerDef: LayerConfig;
|
readonly layerDef: LayerConfig;
|
||||||
}
|
}
|
|
@ -5,7 +5,8 @@ import Translations from "../../UI/i18n/Translations";
|
||||||
import {TagUtils} from "../../Logic/Tags/TagUtils";
|
import {TagUtils} from "../../Logic/Tags/TagUtils";
|
||||||
|
|
||||||
export default class FilterConfig {
|
export default class FilterConfig {
|
||||||
readonly options: {
|
public readonly id: string
|
||||||
|
public readonly options: {
|
||||||
question: Translation;
|
question: Translation;
|
||||||
osmTags: TagsFilter;
|
osmTags: TagsFilter;
|
||||||
}[];
|
}[];
|
||||||
|
@ -14,11 +15,18 @@ export default class FilterConfig {
|
||||||
if (json.options === undefined) {
|
if (json.options === undefined) {
|
||||||
throw `A filter without options was given at ${context}`
|
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) {
|
if (json.options.map === undefined) {
|
||||||
throw `A filter was given where the options aren't a list at ${context}`
|
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) => {
|
this.options = json.options.map((option, i) => {
|
||||||
const question = Translations.T(
|
const question = Translations.T(
|
||||||
option.question,
|
option.question,
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import {AndOrTagConfigJson} from "./TagConfigJson";
|
import {AndOrTagConfigJson} from "./TagConfigJson";
|
||||||
|
|
||||||
export default interface FilterConfigJson {
|
export default interface FilterConfigJson {
|
||||||
|
/**
|
||||||
|
* An id/name for this filter, used to set the URL parameters
|
||||||
|
*/
|
||||||
|
id: string,
|
||||||
/**
|
/**
|
||||||
* The options for a filter
|
* The options for a filter
|
||||||
* If there are multiple options these will be a list of radio buttons
|
* 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 Translations from "../i18n/Translations";
|
||||||
import {Translation} from "../i18n/Translation";
|
import {Translation} from "../i18n/Translation";
|
||||||
import Svg from "../../Svg";
|
import Svg from "../../Svg";
|
||||||
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
|
|
||||||
import {And} from "../../Logic/Tags/And";
|
|
||||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import BaseUIElement from "../BaseUIElement";
|
import BaseUIElement from "../BaseUIElement";
|
||||||
import State from "../../State";
|
import State from "../../State";
|
||||||
|
@ -16,11 +14,6 @@ import FilteredLayer from "../../Models/FilteredLayer";
|
||||||
import BackgroundSelector from "./BackgroundSelector";
|
import BackgroundSelector from "./BackgroundSelector";
|
||||||
import FilterConfig from "../../Models/ThemeConfig/FilterConfig";
|
import FilterConfig from "../../Models/ThemeConfig/FilterConfig";
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows the filter
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default class FilterView extends VariableUiElement {
|
export default class FilterView extends VariableUiElement {
|
||||||
constructor(filteredLayer: UIEventSource<FilteredLayer[]>) {
|
constructor(filteredLayer: UIEventSource<FilteredLayer[]>) {
|
||||||
const backgroundSelector = new Toggle(
|
const backgroundSelector = new Toggle(
|
||||||
|
@ -101,26 +94,52 @@ export default class FilterView extends VariableUiElement {
|
||||||
return undefined;
|
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
|
FilterView.createFilter
|
||||||
);
|
);
|
||||||
|
|
||||||
const update = () => {
|
listFilterElements.forEach((inputElement, i) =>
|
||||||
let listTagsFilters = Utils.NoNull(
|
inputElement[1].addCallback((changed) => {
|
||||||
listFilterElements.map((input) => input[1].data)
|
const oldValue = flayer.appliedFilters.data
|
||||||
);
|
|
||||||
flayer.appliedFilters.setData(new And(listTagsFilters));
|
|
||||||
};
|
|
||||||
|
|
||||||
listFilterElements.forEach((inputElement) =>
|
if(changed === undefined){
|
||||||
inputElement[1].addCallback((_) => update())
|
// 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 => {
|
flayer.appliedFilters.addCallbackAndRun(appliedFilters => {
|
||||||
if (appliedFilters === undefined || appliedFilters.and.length === 0) {
|
for (let i = 0; i < layer.filters.length; i++){
|
||||||
listFilterElements.forEach(filter => filter[1].setData(undefined))
|
const filter = layer.filters[i];
|
||||||
return
|
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")))
|
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) {
|
if (filterConfig.options.length === 1) {
|
||||||
let option = filterConfig.options[0];
|
let option = filterConfig.options[0];
|
||||||
|
|
||||||
|
@ -142,20 +161,36 @@ export default class FilterView extends VariableUiElement {
|
||||||
.ToggleOnClick()
|
.ToggleOnClick()
|
||||||
.SetClass("block m-1")
|
.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;
|
let options = filterConfig.options;
|
||||||
|
|
||||||
|
const values = options.map((f, i) => ({
|
||||||
|
filter: filterConfig, selected: i
|
||||||
|
}))
|
||||||
const radio = new RadioButton(
|
const radio = new RadioButton(
|
||||||
options.map(
|
options.map(
|
||||||
(option) =>
|
(option, i) =>
|
||||||
new FixedInputElement(option.question.Clone(), option.osmTags)
|
new FixedInputElement(option.question.Clone(), i)
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
dontStyle: true
|
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")
|
).SetClass("flex flex-col")
|
||||||
).onClick(() => {
|
).onClick(() => {
|
||||||
preset.layerToAddTo.appliedFilters.setData(new And([]))
|
preset.layerToAddTo.appliedFilters.setData([])
|
||||||
cancel()
|
cancel()
|
||||||
})
|
})
|
||||||
|
|
||||||
const disableFiltersOrConfirm = new Toggle(
|
const disableFiltersOrConfirm = new Toggle(
|
||||||
openLayerOrConfirm,
|
openLayerOrConfirm,
|
||||||
disableFilter,
|
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,
|
"wayHandling": 1,
|
||||||
"filter": [
|
"filter": [
|
||||||
{
|
{
|
||||||
|
"id": "wheelchair",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
@ -275,6 +276,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "shelter",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
|
|
@ -170,6 +170,7 @@
|
||||||
],
|
],
|
||||||
"filter": [
|
"filter": [
|
||||||
{
|
{
|
||||||
|
"id": "opened-now",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
|
|
@ -2627,6 +2627,7 @@
|
||||||
"wayHandling": 1,
|
"wayHandling": 1,
|
||||||
"filter": [
|
"filter": [
|
||||||
{
|
{
|
||||||
|
"id": "vehicle-type",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
@ -2656,6 +2657,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "working",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
@ -2671,6 +2673,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "connection_type",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
|
|
@ -648,6 +648,7 @@
|
||||||
"wayHandling": 1,
|
"wayHandling": 1,
|
||||||
"filter": [
|
"filter": [
|
||||||
{
|
{
|
||||||
|
"id": "vehicle-type",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
@ -677,6 +678,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "working",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
|
|
@ -242,6 +242,7 @@ function run(file, protojson) {
|
||||||
})
|
})
|
||||||
|
|
||||||
proto["filter"].push({
|
proto["filter"].push({
|
||||||
|
id:"connection_type",
|
||||||
options: filterOptions
|
options: filterOptions
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -560,6 +560,7 @@
|
||||||
],
|
],
|
||||||
"filter": [
|
"filter": [
|
||||||
{
|
{
|
||||||
|
"id": "opened-now",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
@ -571,6 +572,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "vegetarian",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
@ -589,6 +591,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "vegan",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
@ -605,6 +608,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "halal",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
|
|
@ -423,6 +423,7 @@
|
||||||
],
|
],
|
||||||
"filter": [
|
"filter": [
|
||||||
{
|
{
|
||||||
|
"id": "access",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
@ -433,6 +434,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "dogs",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
|
|
@ -444,6 +444,7 @@
|
||||||
},
|
},
|
||||||
"filter": [
|
"filter": [
|
||||||
{
|
{
|
||||||
|
"id": "kid-books",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": "Kinderboeken aanwezig?",
|
"question": "Kinderboeken aanwezig?",
|
||||||
|
@ -452,6 +453,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "adult-books",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": "Boeken voor volwassenen aanwezig?",
|
"question": "Boeken voor volwassenen aanwezig?",
|
||||||
|
@ -460,6 +462,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "inside",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": "Binnen of buiten",
|
"question": "Binnen of buiten",
|
||||||
|
|
|
@ -411,6 +411,7 @@
|
||||||
],
|
],
|
||||||
"filter": [
|
"filter": [
|
||||||
{
|
{
|
||||||
|
"id": "wheelchair",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
@ -421,6 +422,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "changing_table",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
@ -431,6 +433,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "free",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": {
|
"question": {
|
||||||
|
|
|
@ -143,6 +143,7 @@
|
||||||
},
|
},
|
||||||
"filter": [
|
"filter": [
|
||||||
{
|
{
|
||||||
|
"id": "name-alt",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": "Name contains 'alt'",
|
"question": "Name contains 'alt'",
|
||||||
|
@ -151,6 +152,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "name-wenslijn",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": "Name contains 'wenslijn'",
|
"question": "Name contains 'wenslijn'",
|
||||||
|
@ -159,6 +161,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "name-omleiding",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": "Name contains 'omleiding'",
|
"question": "Name contains 'omleiding'",
|
||||||
|
@ -167,6 +170,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id":"ref-alt",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": "Reference contains 'alt'",
|
"question": "Reference contains 'alt'",
|
||||||
|
@ -175,6 +179,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "missing_link",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": "No filter"
|
"question": "No filter"
|
||||||
|
@ -194,6 +199,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "proposed",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"question": "No filter"
|
"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://github.com/streetcomplete/StreetComplete/tree/master/res/graphics",
|
||||||
"https://f-droid.org/packages/de.westnordost.streetcomplete/"
|
"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",
|
"maintainer": "Pieter Vander Vennet, Rob Nickerson, Russ Garrett",
|
||||||
"icon": "./assets/themes/uk_addresses/housenumber_unknown.svg",
|
"icon": "./assets/themes/uk_addresses/housenumber_unknown.svg",
|
||||||
"version": "2021-09-17",
|
"version": "2021-09-17",
|
||||||
"startLat": -0.08528530407,
|
"startLat": -0.08706,
|
||||||
"startLon": 51.52103754846,
|
"startLon": 51.52224,
|
||||||
"startZoom": 18,
|
"startZoom": 17,
|
||||||
"widenFactor": 1.5,
|
"widenFactor": 1.01,
|
||||||
"socialImage": "",
|
"socialImage": "",
|
||||||
|
"clustering": {
|
||||||
|
"minNeededFeatures": 25,
|
||||||
|
"maxZoom": 17
|
||||||
|
},
|
||||||
"layers": [
|
"layers": [
|
||||||
{
|
{
|
||||||
"id": "to_import",
|
"id": "to_import",
|
||||||
|
@ -34,21 +38,21 @@
|
||||||
"minzoom": 12,
|
"minzoom": 12,
|
||||||
"wayHandling": 1,
|
"wayHandling": 1,
|
||||||
"icon": {
|
"icon": {
|
||||||
"render": "./assets/themes/uk_addresses/housenumber_unknown.svg"
|
"render": "./assets/themes/uk_addresses/housenumber_unknown.svg",
|
||||||
},
|
|
||||||
"iconSize": {
|
|
||||||
"render": "40,40,center",
|
|
||||||
"mappings": [
|
"mappings": [
|
||||||
{
|
{
|
||||||
"if": "_embedding_object:id~*",
|
"if": "_embedding_object:id~*",
|
||||||
"then": "15,15,center"
|
"then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"if": "_imported=yes",
|
"if": "_imported=yes",
|
||||||
"then": "8,8,center"
|
"then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"iconSize": {
|
||||||
|
"render": "40,40,center"
|
||||||
|
},
|
||||||
"title": {
|
"title": {
|
||||||
"render": "Address to be determined"
|
"render": "Address to be determined"
|
||||||
},
|
},
|
||||||
|
@ -73,6 +77,22 @@
|
||||||
"_embedding_object:addr:housenumber=JSON.parse(feat.properties._embedding_object)?.['addr:housenumber']",
|
"_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:addr:street=JSON.parse(feat.properties._embedding_object)?.['addr:street']",
|
||||||
"_embedding_object:id=JSON.parse(feat.properties._embedding_object)?.id"
|
"_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",
|
["bbox bounds test",
|
||||||
() => {
|
() => {
|
||||||
const bbox = BBox.fromTile(16, 32754, 21785)
|
const bbox = BBox.fromTile(16, 32754, 21785)
|
||||||
equal(-0.0714111328125, bbox.minLon)
|
equal(-0.076904296875, bbox.minLon)
|
||||||
equal(-0.076904296875, bbox.maxLon)
|
equal(-0.0714111328125, bbox.maxLon)
|
||||||
equal(51.53266860674158, bbox.minLat)
|
equal(51.5292513551899, bbox.minLat)
|
||||||
equal(51.5292513551899, bbox.maxLat)
|
equal(51.53266860674158, bbox.maxLat)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in a new issue