forked from MapComplete/MapComplete
Merge develop
This commit is contained in:
commit
0969558b97
74 changed files with 1785 additions and 637 deletions
|
|
@ -10,6 +10,7 @@ import { Changes } from "../Osm/Changes"
|
|||
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
|
||||
import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore"
|
||||
import { WithChangesState } from "../../Models/ThemeViewState/WithChangesState"
|
||||
import Objects from "../../Utils/Objects"
|
||||
|
||||
export default class SelectedElementTagsUpdater {
|
||||
private static readonly metatags = new Set([
|
||||
|
|
@ -160,7 +161,7 @@ export default class SelectedElementTagsUpdater {
|
|||
const newGeometry = osmObject.asGeoJson()?.geometry
|
||||
const oldFeature = state.indexedFeatures.featuresById.data.get(id)
|
||||
const oldGeometry = oldFeature?.geometry
|
||||
if (oldGeometry !== undefined && !Utils.SameObject(newGeometry, oldGeometry)) {
|
||||
if (oldGeometry !== undefined && !Objects.sameObject(newGeometry, oldGeometry)) {
|
||||
console.log("Detected a difference in geometry for ", id)
|
||||
this.invalidateCache(s)
|
||||
oldFeature.geometry = newGeometry
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import TileLocalStorage from "./TileLocalStorage"
|
|||
import { GeoOperations } from "../../GeoOperations"
|
||||
import FeaturePropertiesStore from "./FeaturePropertiesStore"
|
||||
import { UIEventSource } from "../../UIEventSource"
|
||||
import { Utils } from "../../../Utils"
|
||||
import { Tiles } from "../../../Models/TileRange"
|
||||
import { BBox } from "../../BBox"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
class SingleTileSaver {
|
||||
private readonly _storage: UIEventSource<Feature[]>
|
||||
|
|
@ -31,7 +31,7 @@ class SingleTileSaver {
|
|||
}
|
||||
|
||||
public saveFeatures(features: Feature[]) {
|
||||
if (Utils.sameList(features, this._storage.data)) {
|
||||
if (Lists.sameList(features, this._storage.data)) {
|
||||
return
|
||||
}
|
||||
for (const feature of features) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { Stores, UIEventSource } from "../../UIEventSource"
|
|||
import { FeatureSource, IndexedFeatureSource } from "../FeatureSource"
|
||||
import { ChangeDescription, ChangeDescriptionTools } from "../../Osm/Actions/ChangeDescription"
|
||||
import { Feature } from "geojson"
|
||||
import { Utils } from "../../../Utils"
|
||||
import Objects from "../../../Utils/Objects"
|
||||
|
||||
export default class ChangeGeometryApplicator implements FeatureSource {
|
||||
public readonly features: UIEventSource<Feature[]> = new UIEventSource<Feature[]>([])
|
||||
|
|
@ -69,7 +69,7 @@ export default class ChangeGeometryApplicator implements FeatureSource {
|
|||
// We only apply the last change as that one'll have the latest geometry
|
||||
const change = changesForFeature[changesForFeature.length - 1]
|
||||
copy.geometry = ChangeDescriptionTools.getGeojsonGeometry(change)
|
||||
if (Utils.SameObject(copy.geometry, feature.geometry)) {
|
||||
if (Objects.sameObject(copy.geometry, feature.geometry)) {
|
||||
// No actual changes: pass along the original
|
||||
newFeatures.push(feature)
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { BBox } from "../../BBox"
|
|||
import { OsmFeature } from "../../../Models/OsmFeature"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
;("use strict")
|
||||
("use strict")
|
||||
|
||||
/**
|
||||
* A wrapper around the 'Overpass'-object.
|
||||
|
|
@ -229,7 +229,7 @@ export default class OverpassFeatureSource<T extends OsmFeature = OsmFeature> im
|
|||
const requestedBounds = this.state.bounds.data
|
||||
if (
|
||||
this._lastQueryBBox !== undefined &&
|
||||
Utils.sameList(this._layersToDownload.data, this._lastRequestedLayers) &&
|
||||
Lists.sameList(this._layersToDownload.data, this._lastRequestedLayers) &&
|
||||
requestedBounds.isContainedIn(this._lastQueryBBox)
|
||||
) {
|
||||
return undefined
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export default class FilterSearch {
|
|||
.split(" ")
|
||||
.map((query) => {
|
||||
if (!Strings.isEmoji(query)) {
|
||||
return Utils.simplifyStringForSearch(query)
|
||||
return Strings.simplifyStringForSearch(query)
|
||||
}
|
||||
return query
|
||||
})
|
||||
|
|
@ -64,7 +64,7 @@ export default class FilterSearch {
|
|||
option.searchTerms?.["en"] ??
|
||||
[]),
|
||||
].flatMap((term) => [term, ...(term?.split(" ") ?? [])])
|
||||
terms = terms.map((t) => Utils.simplifyStringForSearch(t))
|
||||
terms = terms.map((t) => Strings.simplifyStringForSearch(t))
|
||||
terms.push(option.emoji)
|
||||
Lists.noNullInplace(terms)
|
||||
const distances = queries.flatMap((query) =>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import SearchUtils from "./SearchUtils"
|
|||
import ThemeSearch from "./ThemeSearch"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
|
||||
import { Utils } from "../../Utils"
|
||||
import { Strings } from "../../Utils/Strings"
|
||||
|
||||
export default class LayerSearch {
|
||||
private readonly _theme: ThemeConfig
|
||||
|
|
@ -24,7 +24,7 @@ export default class LayerSearch {
|
|||
const queryParts = query
|
||||
.trim()
|
||||
.split(" ")
|
||||
.map((q) => Utils.simplifyStringForSearch(q))
|
||||
.map((q) => Strings.simplifyStringForSearch(q))
|
||||
for (const id in ThemeSearch.officialThemes.layers) {
|
||||
if (options?.whitelist && !options?.whitelist.has(id)) {
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { GeoOperations } from "../GeoOperations"
|
|||
import { ImmutableStore, Store, Stores } from "../UIEventSource"
|
||||
import OpenStreetMapIdSearch from "./OpenStreetMapIdSearch"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
import { Strings } from "../../Utils/Strings"
|
||||
|
||||
type IntermediateResult = {
|
||||
feature: Feature
|
||||
|
|
@ -61,7 +62,7 @@ export default class LocalElementSearch implements GeocodingProvider {
|
|||
...searchTerms
|
||||
.flatMap((entry) => entry.split(/ /))
|
||||
.map((entry) => {
|
||||
let simplified = Utils.simplifyStringForSearch(entry)
|
||||
let simplified = Strings.simplifyStringForSearch(entry)
|
||||
if (matchStart) {
|
||||
simplified = simplified.slice(0, query.length)
|
||||
}
|
||||
|
|
@ -103,7 +104,7 @@ export default class LocalElementSearch implements GeocodingProvider {
|
|||
const centerPoint: [number, number] = [center.lon, center.lat]
|
||||
const properties = this._state.perLayer
|
||||
const candidateId = OpenStreetMapIdSearch.extractId(query)
|
||||
query = Utils.simplifyStringForSearch(query)
|
||||
query = Strings.simplifyStringForSearch(query)
|
||||
|
||||
const partials: Store<IntermediateResult[]>[] = []
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import Locale from "../../UI/i18n/Locale"
|
|||
import { Utils } from "../../Utils"
|
||||
import ThemeSearch from "./ThemeSearch"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
import { Strings } from "../../Utils/Strings"
|
||||
|
||||
export default class SearchUtils {
|
||||
/** Applies special search terms, such as 'studio', 'osmcha', ...
|
||||
|
|
@ -60,7 +61,7 @@ export default class SearchUtils {
|
|||
const queryParts = query
|
||||
.trim()
|
||||
.split(" ")
|
||||
.map((q) => Utils.simplifyStringForSearch(q))
|
||||
.map((q) => Strings.simplifyStringForSearch(q))
|
||||
let terms: string[]
|
||||
if (Array.isArray(keywords)) {
|
||||
terms = keywords
|
||||
|
|
@ -74,7 +75,7 @@ export default class SearchUtils {
|
|||
const q = queryParts[i]
|
||||
let minDistance: number = 99
|
||||
for (const term of termsAll) {
|
||||
const d = Utils.levenshteinDistance(q, Utils.simplifyStringForSearch(term))
|
||||
const d = Utils.levenshteinDistance(q, Strings.simplifyStringForSearch(term))
|
||||
if (d < minDistance) {
|
||||
minDistance = d
|
||||
}
|
||||
|
|
|
|||
|
|
@ -314,6 +314,14 @@ export class And<T extends TagsFilter = TagsFilter> extends TagsFilter {
|
|||
for (let j = i + 1; j < optimized.length; j++) {
|
||||
const ti = optimized[i]
|
||||
const tj = optimized[j]
|
||||
if (
|
||||
!ti ||
|
||||
!tj ||
|
||||
typeof ti.shadows !== "function" ||
|
||||
typeof tj.shadows !== "function"
|
||||
) {
|
||||
continue
|
||||
}
|
||||
if (ti.shadows(tj)) {
|
||||
// if 'ti' is true, this implies 'tj' is always true as well.
|
||||
// if 'ti' is false, then 'tj' might be true or false
|
||||
|
|
@ -322,6 +330,7 @@ export class And<T extends TagsFilter = TagsFilter> extends TagsFilter {
|
|||
// If 'ti' is true, then 'tj' will be true too and 'tj' can be ignored
|
||||
// If 'ti' is false, then the entire expression will be false and it doesn't matter what 'tj' yields
|
||||
optimized.splice(j, 1)
|
||||
j--
|
||||
} else if (tj.shadows(ti)) {
|
||||
optimized.splice(i, 1)
|
||||
i--
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ export default class SubstitutingTag extends TagsFilter {
|
|||
}
|
||||
|
||||
isNegative(): boolean {
|
||||
return false
|
||||
return this._value === ""
|
||||
}
|
||||
|
||||
visit(f: (tagsFilter: TagsFilter) => void) {
|
||||
|
|
|
|||
|
|
@ -994,11 +994,31 @@ export class TagUtils {
|
|||
].join("\n")
|
||||
}
|
||||
|
||||
static fromProperties(tags: Record<string, string>): TagConfigJson | boolean {
|
||||
public static fromProperties(tags: Record<string, string>): TagConfigJson | boolean {
|
||||
const opt = new And(Object.keys(tags).map((k) => new Tag(k, tags[k]))).optimize()
|
||||
if (opt === true || opt === false) {
|
||||
return opt
|
||||
}
|
||||
return opt.asJson()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a similarly structured tag, but all tags with an empty value are removed.
|
||||
* Those are assumed to be all met (and thus true)
|
||||
*
|
||||
* new And([new Tag("a", "b"), new Tag("c", "")] // => new Tag("a","b")
|
||||
* new And([new Tag("c", "")] // => true
|
||||
*/
|
||||
public static removeEmptyParts(tag: UploadableTag): UploadableTag | true {
|
||||
if (tag["and"]) {
|
||||
const tags = <UploadableTag[]>tag["and"]
|
||||
const cleaned = tags.map(t => TagUtils.removeEmptyParts(t))
|
||||
const filtered = <UploadableTag[]>cleaned.filter(t => t !== true)
|
||||
return new And(filtered)
|
||||
}
|
||||
if (tag.isNegative()) {
|
||||
return true
|
||||
}
|
||||
return tag
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Utils } from "../Utils"
|
||||
import { Readable, Subscriber, Unsubscriber, Updater, Writable } from "svelte/store"
|
||||
import { Lists } from "../Utils/Lists"
|
||||
|
||||
/**
|
||||
* Various static utils
|
||||
|
|
@ -66,7 +67,7 @@ export class Stores {
|
|||
stable.setData(undefined)
|
||||
return
|
||||
}
|
||||
if (Utils.sameList(stable.data, list)) {
|
||||
if (Lists.sameList(stable.data, list)) {
|
||||
return
|
||||
}
|
||||
stable.setData(list)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue