forked from MapComplete/MapComplete
Add filters
This commit is contained in:
parent
965faca0e5
commit
42a6b37ca6
13 changed files with 287 additions and 219 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -18,3 +18,4 @@ missing_translations.txt
|
|||
Svg.ts
|
||||
data/
|
||||
Folder.DotSettings.user
|
||||
index_*.ts
|
|
@ -52,7 +52,7 @@ export default class MapState extends UserRelatedState {
|
|||
* The location as delivered by the GPS
|
||||
*/
|
||||
public currentUserLocation: FeatureSourceForLayer & Tiled;
|
||||
|
||||
|
||||
/**
|
||||
* All previously visited points
|
||||
*/
|
||||
|
@ -82,7 +82,7 @@ export default class MapState extends UserRelatedState {
|
|||
public overlayToggles: { config: TilesourceConfig, isDisplayed: UIEventSource<boolean> }[]
|
||||
|
||||
|
||||
constructor(layoutToUse: LayoutConfig, options?: {attemptLogin: true | boolean}) {
|
||||
constructor(layoutToUse: LayoutConfig, options?: { attemptLogin: true | boolean }) {
|
||||
super(layoutToUse, options);
|
||||
|
||||
this.availableBackgroundLayers = AvailableBaseLayers.AvailableLayersAt(this.locationControl);
|
||||
|
@ -97,7 +97,7 @@ export default class MapState extends UserRelatedState {
|
|||
const self = this
|
||||
this.backgroundLayer = new UIEventSource<BaseLayer>(defaultLayer)
|
||||
this.backgroundLayer.addCallbackAndRunD(layer => self.backgroundLayerId.setData(layer.id))
|
||||
|
||||
|
||||
const attr = new Attribution(
|
||||
this.locationControl,
|
||||
this.osmConnection.userDetails,
|
||||
|
@ -176,11 +176,11 @@ export default class MapState extends UserRelatedState {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
private initCurrentView(){
|
||||
|
||||
private initCurrentView() {
|
||||
let currentViewLayer: FilteredLayer = this.filteredLayers.data.filter(l => l.layerDef.id === "current_view")[0]
|
||||
|
||||
if(currentViewLayer === undefined){
|
||||
if (currentViewLayer === undefined) {
|
||||
// This layer is not needed by the theme and thus unloaded
|
||||
return;
|
||||
}
|
||||
|
@ -188,8 +188,8 @@ export default class MapState extends UserRelatedState {
|
|||
|
||||
let i = 0
|
||||
const self = this;
|
||||
const features : UIEventSource<{ feature: any, freshness: Date }[]>= this.currentBounds.map(bounds => {
|
||||
if(bounds === undefined){
|
||||
const features: UIEventSource<{ feature: any, freshness: Date }[]> = this.currentBounds.map(bounds => {
|
||||
if (bounds === undefined) {
|
||||
return []
|
||||
}
|
||||
i++
|
||||
|
@ -197,14 +197,14 @@ export default class MapState extends UserRelatedState {
|
|||
freshness: new Date(),
|
||||
feature: {
|
||||
type: "Feature",
|
||||
properties:{
|
||||
id:"current_view-"+i,
|
||||
"current_view":"yes",
|
||||
"zoom": ""+self.locationControl.data.zoom
|
||||
properties: {
|
||||
id: "current_view-" + i,
|
||||
"current_view": "yes",
|
||||
"zoom": "" + self.locationControl.data.zoom
|
||||
},
|
||||
geometry:{
|
||||
type:"Polygon",
|
||||
coordinates:[[
|
||||
geometry: {
|
||||
type: "Polygon",
|
||||
coordinates: [[
|
||||
[bounds.maxLon, bounds.maxLat],
|
||||
[bounds.minLon, bounds.maxLat],
|
||||
[bounds.minLon, bounds.minLat],
|
||||
|
@ -216,13 +216,16 @@ export default class MapState extends UserRelatedState {
|
|||
}
|
||||
return [feature]
|
||||
})
|
||||
|
||||
this.currentView = new SimpleFeatureSource(currentViewLayer,0,features)
|
||||
|
||||
this.currentView = new SimpleFeatureSource(currentViewLayer, 0, features)
|
||||
}
|
||||
|
||||
private initGpsLocation() {
|
||||
// Initialize the gps layer data. This is emtpy for now, the actual writing happens in the Geolocationhandler
|
||||
let gpsLayerDef: FilteredLayer = this.filteredLayers.data.filter(l => l.layerDef.id === "gps_location")[0]
|
||||
if(gpsLayerDef === undefined){
|
||||
return
|
||||
}
|
||||
this.currentUserLocation = new SimpleFeatureSource(gpsLayerDef, Tiles.tile_index(0, 0, 0));
|
||||
}
|
||||
|
||||
|
@ -235,7 +238,7 @@ export default class MapState extends UserRelatedState {
|
|||
features.ping()
|
||||
const self = this;
|
||||
let i = 0
|
||||
this.currentUserLocation.features.addCallbackAndRunD(([location]) => {
|
||||
this.currentUserLocation?.features?.addCallbackAndRunD(([location]) => {
|
||||
if (location === undefined) {
|
||||
return;
|
||||
}
|
||||
|
@ -266,7 +269,9 @@ export default class MapState extends UserRelatedState {
|
|||
|
||||
|
||||
let gpsLayerDef: FilteredLayer = this.filteredLayers.data.filter(l => l.layerDef.id === "gps_location_history")[0]
|
||||
this.historicalUserLocations = new SimpleFeatureSource(gpsLayerDef, Tiles.tile_index(0, 0, 0), features);
|
||||
if(gpsLayerDef !== undefined){
|
||||
this.historicalUserLocations = new SimpleFeatureSource(gpsLayerDef, Tiles.tile_index(0, 0, 0), features);
|
||||
}
|
||||
|
||||
|
||||
const asLine = features.map(allPoints => {
|
||||
|
@ -294,7 +299,9 @@ export default class MapState extends UserRelatedState {
|
|||
}]
|
||||
})
|
||||
let gpsLineLayerDef: FilteredLayer = this.filteredLayers.data.filter(l => l.layerDef.id === "gps_track")[0]
|
||||
this.historicalUserLocationsTrack = new SimpleFeatureSource(gpsLineLayerDef, Tiles.tile_index(0, 0, 0), asLine);
|
||||
if(gpsLineLayerDef !== undefined){
|
||||
this.historicalUserLocationsTrack = new SimpleFeatureSource(gpsLineLayerDef, Tiles.tile_index(0, 0, 0), asLine);
|
||||
}
|
||||
}
|
||||
|
||||
private initHomeLocation() {
|
||||
|
@ -331,7 +338,9 @@ export default class MapState extends UserRelatedState {
|
|||
})
|
||||
|
||||
const flayer = this.filteredLayers.data.filter(l => l.layerDef.id === "home_location")[0]
|
||||
this.homeLocation = new SimpleFeatureSource(flayer, Tiles.tile_index(0, 0, 0), feature)
|
||||
if (flayer !== undefined) {
|
||||
this.homeLocation = new SimpleFeatureSource(flayer, Tiles.tile_index(0, 0, 0), feature)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -349,19 +358,19 @@ export default class MapState extends UserRelatedState {
|
|||
} else {
|
||||
isDisplayed = QueryParameters.GetBooleanQueryParameter(
|
||||
"layer-" + layer.id,
|
||||
""+layer.shownByDefault,
|
||||
"" + layer.shownByDefault,
|
||||
"Wether or not layer " + layer.id + " is shown"
|
||||
)
|
||||
}
|
||||
const flayer : FilteredLayer = {
|
||||
const flayer: FilteredLayer = {
|
||||
isDisplayed: isDisplayed,
|
||||
layerDef: layer,
|
||||
appliedFilters: new UIEventSource<Map<string, FilterState>>(new Map<string, FilterState>())
|
||||
appliedFilters: new UIEventSource<Map<string, FilterState>>(new Map<string, FilterState>())
|
||||
};
|
||||
layer.filters.forEach(filterConfig => {
|
||||
const stateSrc = filterConfig.initState()
|
||||
|
||||
stateSrc .addCallbackAndRun(state => flayer.appliedFilters.data.set(filterConfig.id, state))
|
||||
|
||||
stateSrc.addCallbackAndRun(state => flayer.appliedFilters.data.set(filterConfig.id, state))
|
||||
flayer.appliedFilters.map(dict => dict.get(filterConfig.id))
|
||||
.addCallback(state => stateSrc.setData(state))
|
||||
})
|
||||
|
|
|
@ -56,9 +56,6 @@ export class TagUtils {
|
|||
|
||||
/***
|
||||
* Creates a hash {key --> [values : string | Regex ]}, with all the values present in the tagsfilter
|
||||
*
|
||||
* @param tagsFilters
|
||||
* @constructor
|
||||
*/
|
||||
static SplitKeys(tagsFilters: TagsFilter[], allowRegex = false) {
|
||||
const keyValues = {} // Map string -> string[]
|
||||
|
@ -189,16 +186,26 @@ export class TagUtils {
|
|||
if (tag.indexOf(operator) >= 0) {
|
||||
const split = Utils.SplitFirst(tag, operator);
|
||||
|
||||
const val = Number(split[1].trim())
|
||||
let val = Number(split[1].trim())
|
||||
if (isNaN(val)) {
|
||||
throw `Error: not a valid value for a comparison: ${split[1]}, make sure it is a number and nothing more (at ${context})`
|
||||
val = new Date(split[1].trim()).getTime()
|
||||
}
|
||||
|
||||
const f = (value: string | undefined) => {
|
||||
const b = Number(value?.replace(/[^\d.]/g, ''))
|
||||
if (isNaN(b)) {
|
||||
console.log("Comparing ",value,operator,val)
|
||||
if(value === undefined){
|
||||
return false;
|
||||
}
|
||||
let b = Number(value?.trim() )
|
||||
if (isNaN(b)) {
|
||||
if(value.endsWith(" UTC")) {
|
||||
value = value.replace(" UTC", "+00")
|
||||
}
|
||||
b = new Date(value).getTime()
|
||||
if(isNaN(b)){
|
||||
return false
|
||||
}
|
||||
}
|
||||
return comparator(b, val)
|
||||
}
|
||||
return new ComparingTag(split[0], f, operator + val)
|
||||
|
@ -259,8 +266,8 @@ export class TagUtils {
|
|||
}
|
||||
if (tag.indexOf("~") >= 0) {
|
||||
const split = Utils.SplitFirst(tag, "~");
|
||||
if(split[1] === "") {
|
||||
throw "Detected a regextag with an empty regex; this is not allowed. Use '"+split[0]+"='instead (at "+context+")"
|
||||
if (split[1] === "") {
|
||||
throw "Detected a regextag with an empty regex; this is not allowed. Use '" + split[0] + "='instead (at " + context + ")"
|
||||
}
|
||||
if (split[1] === "*") {
|
||||
split[1] = "..*"
|
||||
|
|
|
@ -42,12 +42,11 @@ export default class FilterConfig {
|
|||
`${ctx}.question`
|
||||
);
|
||||
let osmTags = undefined;
|
||||
if (option.osmTags !== undefined) {
|
||||
if ((option.fields?.length ?? 0) == 0 && option.osmTags !== undefined) {
|
||||
osmTags = TagUtils.Tag(
|
||||
option.osmTags,
|
||||
`${ctx}.osmTags`
|
||||
);
|
||||
|
||||
}
|
||||
if (question === undefined) {
|
||||
throw `Invalid filter: no question given at ${ctx}`
|
||||
|
@ -67,10 +66,6 @@ export default class FilterConfig {
|
|||
}
|
||||
})
|
||||
|
||||
if (fields.length > 0) {
|
||||
// erase the tags, they aren't needed
|
||||
osmTags = undefined
|
||||
}
|
||||
|
||||
return {question: question, osmTags: osmTags, fields, originalTagsSpec: option.osmTags};
|
||||
});
|
||||
|
@ -92,6 +87,7 @@ export default class FilterConfig {
|
|||
}
|
||||
return "" + state.state
|
||||
}
|
||||
|
||||
const defaultValue = this.options.length > 1 ? "0" : ""
|
||||
const qp = QueryParameters.GetQueryParameter("filter-" + this.id, defaultValue, "State of filter " + this.id)
|
||||
|
||||
|
@ -130,13 +126,14 @@ export default class FilterConfig {
|
|||
return v
|
||||
}
|
||||
for (const key in props) {
|
||||
v = (<string>v).replace("{"+key+"}", props[key])
|
||||
v = (<string>v).replace("{" + key + "}", props[key])
|
||||
}
|
||||
return v
|
||||
}
|
||||
)
|
||||
const parsed = TagUtils.Tag(rewrittenTags)
|
||||
return <FilterState>{
|
||||
currentFilter: TagUtils.Tag(rewrittenTags),
|
||||
currentFilter: parsed,
|
||||
state: str
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
|
@ -22,7 +22,6 @@ import Title from "../../UI/Base/Title";
|
|||
import List from "../../UI/Base/List";
|
||||
import Link from "../../UI/Base/Link";
|
||||
import {Utils} from "../../Utils";
|
||||
import {tag} from "@turf/turf";
|
||||
|
||||
export default class LayerConfig extends WithContextLoader {
|
||||
|
||||
|
|
|
@ -195,7 +195,7 @@ export default class FilterView extends VariableUiElement {
|
|||
}
|
||||
const props = properties.data
|
||||
// Replace all the field occurences in the tags...
|
||||
const tagsSpec = Utils.WalkJson(filter.originalTagsSpec,
|
||||
const tagsSpec = Utils.WalkJson(filter.originalTagsSpec,
|
||||
v => {
|
||||
if (typeof v !== "string") {
|
||||
return v
|
||||
|
|
|
@ -35,7 +35,7 @@ export default class SimpleDatePicker extends InputElement<string> {
|
|||
}
|
||||
|
||||
IsValid(t: string): boolean {
|
||||
return false;
|
||||
return !isNaN(new Date(t).getTime());
|
||||
}
|
||||
|
||||
protected InnerConstructElement(): HTMLElement {
|
||||
|
|
|
@ -206,8 +206,7 @@ export default class ValidatedTextField {
|
|||
"date",
|
||||
"A date",
|
||||
(str) => {
|
||||
const time = Date.parse(str);
|
||||
return !isNaN(time);
|
||||
return !isNaN(new Date(str).getTime());
|
||||
},
|
||||
(str) => {
|
||||
const d = new Date(str);
|
||||
|
|
206
assets/layers/note/note.json
Normal file
206
assets/layers/note/note.json
Normal file
|
@ -0,0 +1,206 @@
|
|||
{
|
||||
"id": "note",
|
||||
"name": {
|
||||
"en": "OpenStreetMap notes"
|
||||
},
|
||||
"description": "This layer shows notes on OpenStreetMap.",
|
||||
"source": {
|
||||
"osmTags": "id~*",
|
||||
"geoJson": "https://api.openstreetmap.org/api/0.6/notes.json?closed=7&bbox={x_min},{y_min},{x_max},{y_max}",
|
||||
"geoJsonZoomLevel": 12,
|
||||
"maxCacheAge": 0
|
||||
},
|
||||
"minzoom": 10,
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Note"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "closed_at~*",
|
||||
"then": {
|
||||
"en": "Closed note"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"calculatedTags": [
|
||||
"_first_comment:=feat.get('comments')[0].text.toLowerCase()",
|
||||
"_opened_by_anonymous_user:=feat.get('comments')[0].user === undefined",
|
||||
"_first_user:=feat.get('comments')[0].user",
|
||||
"_first_user_lc:=feat.get('comments')[0].user?.toLowerCase()",
|
||||
"_first_user_id:=feat.get('comments')[0].uid"
|
||||
],
|
||||
"titleIcons": [
|
||||
{
|
||||
"render": "<a href='https://openstreetmap.org/note/{id}' target='_blank'><img src='./assets/svg/osm-logo-us.svg'></a>"
|
||||
}
|
||||
],
|
||||
"tagRenderings": [
|
||||
{
|
||||
"id": "conversation",
|
||||
"render": "{visualize_note_comments()}"
|
||||
},
|
||||
{
|
||||
"id": "add_image",
|
||||
"render": "{add_image_to_note()}"
|
||||
},
|
||||
{
|
||||
"id": "comment",
|
||||
"render": "{add_note_comment()}"
|
||||
},
|
||||
{
|
||||
"id": "report-contributor",
|
||||
"render": {
|
||||
"en": "<a href='https://www.openstreetmap.org/reports/new?reportable_id={_first_user_id}&reportable_type=User' target='_blank' class='subtle'>Report {_first_user} as spam</a>"
|
||||
},
|
||||
"condition": "_opened_by_anonymous_user=false"
|
||||
},
|
||||
{
|
||||
"id": "report-note",
|
||||
"render": {
|
||||
"en": "<a href='https://www.openstreetmap.org/reports/new?reportable_id={id}&reportable_type=Note' target='_blank'>Report this note as spam or inappropriate</a>"
|
||||
}
|
||||
}
|
||||
],
|
||||
"mapRendering": [
|
||||
{
|
||||
"location": [
|
||||
"point",
|
||||
"centroid"
|
||||
],
|
||||
"icon": {
|
||||
"render": "./assets/svg/note.svg",
|
||||
"mappings": [
|
||||
{
|
||||
"if": "closed_at~*",
|
||||
"then": "./assets/svg/resolved.svg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"iconSize": "40,40,bottom"
|
||||
}
|
||||
],
|
||||
"filter": [
|
||||
{
|
||||
"id": "search",
|
||||
"options": [
|
||||
{
|
||||
"osmTags": "_first_comment~.*{search}.*",
|
||||
"fields": [
|
||||
{
|
||||
"name": "search"
|
||||
}
|
||||
],
|
||||
"question": {
|
||||
"en": "Should mention {search} in the first comment"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "not",
|
||||
"options": [
|
||||
{
|
||||
"osmTags": "_first_comment!~.*{search}.*",
|
||||
"fields": [
|
||||
{
|
||||
"name": "search"
|
||||
}
|
||||
],
|
||||
"question": {
|
||||
"en": "Should <b>not</b> mention {search} in the first comment"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "opened_by",
|
||||
"options": [
|
||||
{
|
||||
"osmTags": "_first_user_lc~.*{search}.*",
|
||||
"fields": [
|
||||
{
|
||||
"name": "search"
|
||||
}
|
||||
],
|
||||
"question": {
|
||||
"en": "Opened by {search}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "not_opened_by",
|
||||
"options": [
|
||||
{
|
||||
"osmTags": "_first_user_lc!~.*{search}.*",
|
||||
"fields": [
|
||||
{
|
||||
"name": "search"
|
||||
}
|
||||
],
|
||||
"question": {
|
||||
"en": "<b>Not</b> opened by {search}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "opened_before",
|
||||
"options": [
|
||||
{
|
||||
"osmTags": "date_created<{search}",
|
||||
"fields": [
|
||||
{
|
||||
"name": "search",
|
||||
"type": "date"
|
||||
}
|
||||
],
|
||||
"question": {
|
||||
"en": "Opened before {search}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "opened_after",
|
||||
"options": [
|
||||
{
|
||||
"osmTags": "date_created>{search}",
|
||||
"fields": [
|
||||
{
|
||||
"name": "search",
|
||||
"type": "date"
|
||||
}
|
||||
],
|
||||
"question": {
|
||||
"en": "Opened after {search}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "anonymous",
|
||||
"options": [
|
||||
{
|
||||
"osmTags": "_opened_by_anonymous_user=true",
|
||||
"question": {
|
||||
"en": "Opened by anonymous user"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "is_open",
|
||||
"options": [
|
||||
{
|
||||
"osmTags": "closed_at=",
|
||||
"question": {
|
||||
"en": "Only show open notes"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -14,166 +14,6 @@
|
|||
"clustering": false,
|
||||
"enableDownload": true,
|
||||
"layers": [
|
||||
{
|
||||
"id": "notes",
|
||||
"name": {
|
||||
"en": "OpenStreetMap notes"
|
||||
},
|
||||
"description": "This layer shows notes on OpenStreetMap.",
|
||||
"source": {
|
||||
"osmTags": "id~*",
|
||||
"geoJson": "https://api.openstreetmap.org/api/0.6/notes.json?closed=7&bbox={x_min},{y_min},{x_max},{y_max}",
|
||||
"geoJsonZoomLevel": 12,
|
||||
"maxCacheAge": 0
|
||||
},
|
||||
"minzoom": 8,
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Note"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "closed_at~*",
|
||||
"then": {
|
||||
"en": "Closed note"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"calculatedTags": [
|
||||
"_first_comment:=feat.get('comments')[0].text.toLowerCase()",
|
||||
"_opened_by_anonymous_user:=feat.get('comments')[0].user === undefined",
|
||||
"_first_user:=feat.get('comments')[0].user",
|
||||
"_first_user_lc:=feat.get('comments')[0].user?.toLowerCase()",
|
||||
"_first_user_id:=feat.get('comments')[0].uid"
|
||||
],
|
||||
"titleIcons": [
|
||||
{
|
||||
"render": "<a href='https://openstreetmap.org/note/{id}' target='_blank'><img src='./assets/svg/osm-logo-us.svg'></a>"
|
||||
}
|
||||
],
|
||||
"tagRenderings": [
|
||||
{
|
||||
"id": "conversation",
|
||||
"render": "{visualize_note_comments()}"
|
||||
},
|
||||
{
|
||||
"id": "add_image",
|
||||
"render": "{add_image_to_note()}"
|
||||
},
|
||||
{
|
||||
"id": "comment",
|
||||
"render": "{add_note_comment()}"
|
||||
},
|
||||
{
|
||||
"id": "report-contributor",
|
||||
"render": {
|
||||
"en": "<a href='https://www.openstreetmap.org/reports/new?reportable_id={_first_user_id}&reportable_type=User' target='_blank' class='subtle'>Report {_first_user} as spam</a>"
|
||||
},
|
||||
"condition": "_opened_by_anonymous_user=false"
|
||||
},
|
||||
{
|
||||
"id": "report-note",
|
||||
"render": {
|
||||
"en": "<a href='https://www.openstreetmap.org/reports/new?reportable_id={id}&reportable_type=Note' target='_blank'>Report this note as spam or inappropriate</a>"
|
||||
}
|
||||
}
|
||||
],
|
||||
"mapRendering": [
|
||||
{
|
||||
"location": [
|
||||
"point",
|
||||
"centroid"
|
||||
],
|
||||
"icon": {
|
||||
"render": "./assets/svg/note.svg",
|
||||
"mappings": [
|
||||
{
|
||||
"if": "closed_at~*",
|
||||
"then": "./assets/svg/resolved.svg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"iconSize": "40,40,bottom"
|
||||
}
|
||||
],
|
||||
"filter": [
|
||||
{
|
||||
"id": "search",
|
||||
"options": [
|
||||
{
|
||||
"osmTags": "_first_comment~.*{search}.*",
|
||||
"fields": [
|
||||
{
|
||||
"name": "search"
|
||||
}
|
||||
],
|
||||
"question": {
|
||||
"en": "Should mention {search} in the first comment"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "not",
|
||||
"options": [
|
||||
{
|
||||
"osmTags": "_first_comment!~.*{search}.*",
|
||||
"fields": [
|
||||
{
|
||||
"name": "search"
|
||||
}
|
||||
],
|
||||
"question": {
|
||||
"en": "Should <b>not</b> mention {search} in the first comment"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "opened_by",
|
||||
"options": [
|
||||
{
|
||||
"osmTags": "_first_user_lc~.*{search}.*",
|
||||
"fields": [
|
||||
{
|
||||
"name": "search"
|
||||
}
|
||||
],
|
||||
"question": {
|
||||
"en": "Opened by {search}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "not_opened_by",
|
||||
"options": [
|
||||
{
|
||||
"osmTags": "_first_user_lc!~.*{search}.*",
|
||||
"fields": [
|
||||
{
|
||||
"name": "search"
|
||||
}
|
||||
],
|
||||
"question": {
|
||||
"en": "<b>Not</b> opened by {search}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "anonymous",
|
||||
"options": [
|
||||
{
|
||||
"osmTags": "_opened_by_anonymous_user=true",
|
||||
"question": {
|
||||
"en": "Opened by anonymous user"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
"note"
|
||||
]
|
||||
}
|
|
@ -8,12 +8,16 @@ rm -rf .cache
|
|||
mkdir dist 2> /dev/null
|
||||
mkdir dist/assets 2> /dev/null
|
||||
|
||||
npm run generate
|
||||
npm run test
|
||||
# This script ends every line with '&&' to chain everything. A failure will thus stop the build
|
||||
npm run generate:editor-layer-index
|
||||
npm run generate:translations
|
||||
npm run generate &&
|
||||
npm run test &&
|
||||
npm run generate:layouts
|
||||
|
||||
if [ $? -ne 0]; then;
|
||||
echo "ERROR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Copy the layer files, as these might contain assets (e.g. svgs)
|
||||
cp -r assets/layers/ dist/assets/layers/
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
import T from "./TestHelper";
|
||||
import {Utils} from "../Utils";
|
||||
import ReplaceGeometryAction from "../Logic/Osm/Actions/ReplaceGeometryAction";
|
||||
import FeaturePipeline from "../Logic/FeatureSource/FeaturePipeline";
|
||||
import {Tag} from "../Logic/Tags/Tag";
|
||||
import MapState from "../Logic/State/MapState";
|
||||
import * as grb from "../assets/themes/grb_import/grb.json"
|
||||
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig";
|
||||
import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
|
||||
import State from "../State";
|
||||
import {BBox} from "../Logic/BBox";
|
||||
import Minimap from "../UI/Base/Minimap";
|
||||
|
@ -33,7 +29,11 @@ export default class ReplaceGeometrySpec extends T {
|
|||
},
|
||||
"layers": [
|
||||
{
|
||||
"builtin": "type_node",
|
||||
"id": "type_node",
|
||||
source:{
|
||||
osmTags:"type=node"
|
||||
},
|
||||
mapRendering: null,
|
||||
"override": {
|
||||
"calculatedTags": [
|
||||
"_is_part_of_building=feat.get('parent_ways')?.some(p => p.building !== undefined && p.building !== '') ?? false",
|
||||
|
@ -266,7 +266,6 @@ export default class ReplaceGeometrySpec extends T {
|
|||
}
|
||||
]
|
||||
},
|
||||
"address",
|
||||
{
|
||||
"id": "grb",
|
||||
"description": "Geometry which comes from GRB with tools to import them",
|
||||
|
|
|
@ -516,7 +516,14 @@ export default class TagSpec extends T {
|
|||
const filter = TagUtils.Tag("_key~*")
|
||||
T.isTrue(filter.matchesProperties(properties), "Lazy value not matched")
|
||||
}
|
||||
]]);
|
||||
],
|
||||
["test date comparison",() => {
|
||||
|
||||
const filter = TagUtils.Tag("date_created<2022-01-07")
|
||||
T.isFalse(filter.matchesProperties({"date_created":"2022-01-08"}), "Date comparison: expected a match")
|
||||
T.isTrue(filter.matchesProperties({"date_created":"2022-01-01"}), "Date comparison: didn't expect a match")
|
||||
|
||||
}]]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue