Chore: housekeeping

This commit is contained in:
Pieter Vander Vennet 2024-06-20 04:21:29 +02:00
parent 8178c5607b
commit cd0d275965
73 changed files with 2105 additions and 2219 deletions

View file

@ -55,16 +55,12 @@ export default class InitialMapPositioning {
layoutToUse?.startZoom ?? 1,
"The initial/current zoom level"
)
const defaultLat =layoutToUse?.startLat ?? 0
const lat = localStorageSynced(
"lat",
defaultLat ,
"The initial/current latitude"
)
const defaultLat = layoutToUse?.startLat ?? 0
const lat = localStorageSynced("lat", defaultLat, "The initial/current latitude")
const defaultLon = layoutToUse?.startLon ?? 0
const lon = localStorageSynced(
"lon",
defaultLon ,
defaultLon,
"The initial/current longitude of the app"
)
@ -92,16 +88,21 @@ export default class InitialMapPositioning {
const [lat, lon] = osmObject.centerpoint()
this.location.setData({ lon, lat })
})
} else if (Constants.GeoIpServer && lat.data === defaultLat && lon.data === defaultLon && !Utils.runningFromConsole) {
} else if (
Constants.GeoIpServer &&
lat.data === defaultLat &&
lon.data === defaultLon &&
!Utils.runningFromConsole
) {
console.log("Using geoip to determine start location...")
// We use geo-IP to zoom to some location
Utils.downloadJson<{ latitude: number, longitude: number }>(
Utils.downloadJson<{ latitude: number; longitude: number }>(
Constants.GeoIpServer + "ip"
).then(({ longitude, latitude }) => {
if(geolocationState.currentGPSLocation.data !== undefined){
if (geolocationState.currentGPSLocation.data !== undefined) {
return // We got a geolocation by now, abort
}
console.log("Setting location based on geoip", longitude, latitude)
console.log("Setting location based on geoip", longitude, latitude)
this.zoom.setData(8)
this.location.setData({ lon: longitude, lat: latitude })
})

View file

@ -163,24 +163,31 @@ export default class DetermineLayout {
return dict
}
private static getSharedTagRenderingOrder(): string[] {
return questions.tagRenderings.map(tr => tr.id)
return questions.tagRenderings.map((tr) => tr.id)
}
private static prepCustomTheme(json: any, sourceUrl?: string, forceId?: string): LayoutConfig {
if (json.layers === undefined && json.tagRenderings !== undefined) {
// We got fed a layer instead of a theme
const layerConfig = <LayerConfigJson>json
const icon = Utils.NoNull(layerConfig.pointRendering.flatMap(
pr => pr.marker
).map(iconSpec => {
const icon = new TagRenderingConfig(<TagRenderingConfigJson>iconSpec.icon).render.txt
if(iconSpec.color === undefined || icon.startsWith("http:") || icon.startsWith("https:")){
return icon
}
const color = new TagRenderingConfig(<TagRenderingConfigJson>iconSpec.color).render.txt
return icon+":"+color
})).join(";")
const icon = Utils.NoNull(
layerConfig.pointRendering
.flatMap((pr) => pr.marker)
.map((iconSpec) => {
const icon = new TagRenderingConfig(<TagRenderingConfigJson>iconSpec.icon)
.render.txt
if (
iconSpec.color === undefined ||
icon.startsWith("http:") ||
icon.startsWith("https:")
) {
return icon
}
const color = new TagRenderingConfig(<TagRenderingConfigJson>iconSpec.color)
.render.txt
return icon + ":" + color
})
).join(";")
json = {
id: json.id,

View file

@ -11,14 +11,17 @@ import { FeatureSource } from "../FeatureSource"
* Highly specialized feature source.
* Based on a lon/lat UIEVentSource, will generate the corresponding feature with the correct properties
*/
export class LastClickFeatureSource implements FeatureSource{
export class LastClickFeatureSource implements FeatureSource {
public readonly renderings: string[]
private i: number = 0
private readonly hasPresets: boolean
private readonly hasNoteLayer: boolean
public static readonly newPointElementId = "new_point_dialog"
public readonly features: Store<Feature[]>
constructor(layout: LayoutConfig, clickSource: Store<{lon:number,lat:number,mode:"left"|"right"|"middle"}> ) {
constructor(
layout: LayoutConfig,
clickSource: Store<{ lon: number; lat: number; mode: "left" | "right" | "middle" }>
) {
this.hasNoteLayer = layout.hasNoteLayer()
this.hasPresets = layout.hasPresets()
const allPresets: BaseUIElement[] = []
@ -33,7 +36,7 @@ export class LastClickFeatureSource implements FeatureSource{
}
const { html } = rendering.RenderIcon(tags, {
noSize: true,
includeBadges: false
includeBadges: false,
})
allPresets.push(html)
}
@ -44,12 +47,16 @@ export class LastClickFeatureSource implements FeatureSource{
)
)
this.features = clickSource.mapD(({lon, lat,mode}) =>
[this.createFeature(lon, lat, mode)])
this.features = clickSource.mapD(({ lon, lat, mode }) => [
this.createFeature(lon, lat, mode),
])
}
public createFeature(lon: number, lat: number, mode?: "left" | "right" | "middle"): Feature<Point, OsmTags> {
public createFeature(
lon: number,
lat: number,
mode?: "left" | "right" | "middle"
): Feature<Point, OsmTags> {
const properties: OsmTags = {
id: LastClickFeatureSource.newPointElementId + "_" + this.i,
has_note_layer: this.hasNoteLayer ? "yes" : "no",
@ -57,7 +64,7 @@ export class LastClickFeatureSource implements FeatureSource{
renderings: this.renderings.join(""),
number_of_presets: "" + this.renderings.length,
first_preset: this.renderings[0],
mouse_button: mode ?? "none"
mouse_button: mode ?? "none",
}
this.i++
@ -66,8 +73,8 @@ export class LastClickFeatureSource implements FeatureSource{
properties,
geometry: {
type: "Point",
coordinates: [lon, lat]
}
coordinates: [lon, lat],
},
}
}
}

View file

@ -85,16 +85,19 @@ export default class MetaTagging {
console.log("Binding an updater to", feature)
let updateCount = 0
tags?.addCallbackD(() => {
console.log("Received an update! Re-calculating the metatags, timediff:", new Date().getTime() - lastUpdateMoment.getTime())
console.log(
"Received an update! Re-calculating the metatags, timediff:",
new Date().getTime() - lastUpdateMoment.getTime()
)
if (feature !== state.selectedElement.data) {
return true // Unregister, we are not the selected element anymore
}
if (new Date().getTime() - lastUpdateMoment.getTime() < (250 + updateCount * 50)) {
if (new Date().getTime() - lastUpdateMoment.getTime() < 250 + updateCount * 50) {
return
}
updateCount ++
updateCount++
lastUpdateMoment = new Date()
window.requestIdleCallback(() => {
this.updateCurrentSelectedElement()

View file

@ -1,14 +1,42 @@
import { Utils } from "../../Utils"
/** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */
export class ThemeMetaTagging {
public static readonly themeName = "usersettings"
public static readonly themeName = "usersettings"
public metaTaggging_for_usersettings(feat: {properties: Record<string, string>}) {
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) )
Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/&lt;/g,'<')?.replace(/&gt;/g,'>') ?? '' )
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href }) (feat) )
Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat) )
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a )
feat.properties['__current_backgroun'] = 'initial_value'
}
}
public metaTaggging_for_usersettings(feat: { properties: Record<string, string> }) {
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () =>
feat.properties._description
.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)
?.at(1)
)
Utils.AddLazyProperty(
feat.properties,
"_d",
() => feat.properties._description?.replace(/&lt;/g, "<")?.replace(/&gt;/g, ">") ?? ""
)
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () =>
((feat) => {
const e = document.createElement("div")
e.innerHTML = feat.properties._d
return Array.from(e.getElementsByTagName("a")).filter(
(a) => a.href.match(/mastodon|en.osm.town/) !== null
)[0]?.href
})(feat)
)
Utils.AddLazyProperty(feat.properties, "_mastodon_link", () =>
((feat) => {
const e = document.createElement("div")
e.innerHTML = feat.properties._d
return Array.from(e.getElementsByTagName("a")).filter(
(a) => a.getAttribute("rel")?.indexOf("me") >= 0
)[0]?.href
})(feat)
)
Utils.AddLazyProperty(
feat.properties,
"_mastodon_candidate",
() => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a
)
feat.properties["__current_backgroun"] = "initial_value"
}
}

View file

@ -27,23 +27,23 @@ export default class LinkedDataLoader {
opening_hours: { "@id": "http://schema.org/openingHoursSpecification" },
openingHours: { "@id": "http://schema.org/openingHours", "@container": "@set" },
geo: { "@id": "http://schema.org/geo" },
alt_name: { "@id": "http://schema.org/alternateName" }
alt_name: { "@id": "http://schema.org/alternateName" },
}
private static COMPACTING_CONTEXT_OH = {
dayOfWeek: { "@id": "http://schema.org/dayOfWeek", "@container": "@set" },
closes: {
"@id": "http://schema.org/closes",
"@type": "http://www.w3.org/2001/XMLSchema#time"
"@type": "http://www.w3.org/2001/XMLSchema#time",
},
opens: {
"@id": "http://schema.org/opens",
"@type": "http://www.w3.org/2001/XMLSchema#time"
}
"@type": "http://www.w3.org/2001/XMLSchema#time",
},
}
private static formatters: Record<"phone" | "email" | "website", Validator> = {
phone: new PhoneValidator(),
email: new EmailValidator(),
website: new UrlValidator(undefined, undefined, true)
website: new UrlValidator(undefined, undefined, true),
}
private static ignoreKeys = [
"http://schema.org/logo",
@ -56,7 +56,7 @@ export default class LinkedDataLoader {
"http://schema.org/description",
"http://schema.org/hasMap",
"http://schema.org/priceRange",
"http://schema.org/contactPoint"
"http://schema.org/contactPoint",
]
private static shapeToPolygon(str: string): Polygon {
@ -69,8 +69,8 @@ export default class LinkedDataLoader {
.trim()
.split(" ")
.map((n) => Number(n))
)
]
),
],
}
}
@ -92,18 +92,18 @@ export default class LinkedDataLoader {
const context = {
lat: {
"@id": "http://schema.org/latitude",
"@type": "http://www.w3.org/2001/XMLSchema#double"
"@type": "http://www.w3.org/2001/XMLSchema#double",
},
lon: {
"@id": "http://schema.org/longitude",
"@type": "http://www.w3.org/2001/XMLSchema#double"
}
"@type": "http://www.w3.org/2001/XMLSchema#double",
},
}
const flattened = await jsonld.compact(geo, context)
return {
type: "Point",
coordinates: [Number(flattened.lon), Number(flattened.lat)]
coordinates: [Number(flattened.lon), Number(flattened.lat)],
}
}
@ -145,7 +145,7 @@ export default class LinkedDataLoader {
Looking at you, C&A!
view-source:https://www.c-and-a.com/stores/be-en/oost-vlaanderen/sint-niklaas/stationsstraat-100.html
* */
parts = parts.filter(p => !p.match(/.. 00:00-00:00/))
parts = parts.filter((p) => !p.match(/.. 00:00-00:00/))
// actually the same as OSM-oh
return OH.simplify(parts.join(";"))
}
@ -247,18 +247,16 @@ export default class LinkedDataLoader {
return await LinkedDataLoader.compact(data, options)
}
let htmlContent = await Utils.download(url)
const div = document.createElement("div")
div.innerHTML = htmlContent
const script = Array.from(div.getElementsByTagName("script"))
.find(script => script.type === "application/ld+json")
const script = Array.from(div.getElementsByTagName("script")).find(
(script) => script.type === "application/ld+json"
)
const snippet = JSON.parse(script.textContent)
snippet["@base"] = url
return await LinkedDataLoader.compact(snippet, options)
}
/**
@ -309,7 +307,7 @@ export default class LinkedDataLoader {
if (properties["latitude"] && properties["longitude"]) {
geometry = {
type: "Point",
coordinates: [Number(properties["longitude"]), Number(properties["latitude"])]
coordinates: [Number(properties["longitude"]), Number(properties["latitude"])],
}
delete properties["latitude"]
delete properties["longitude"]
@ -321,7 +319,7 @@ export default class LinkedDataLoader {
const geo: GeoJSON = {
type: "Feature",
properties,
geometry
geometry,
}
delete linkedData.geo
delete properties.shape
@ -439,7 +437,7 @@ export default class LinkedDataLoader {
"brede publiek",
"iedereen",
"bezoekers",
"iedereen - vooral bezoekers gemeentehuis of bibliotheek."
"iedereen - vooral bezoekers gemeentehuis of bibliotheek.",
].indexOf(audience.toLowerCase()) >= 0
) {
return "yes"
@ -522,7 +520,7 @@ export default class LinkedDataLoader {
mv: "http://schema.mobivoc.org/",
gr: "http://purl.org/goodrelations/v1#",
vp: "https://data.velopark.be/openvelopark/vocabulary#",
vpt: "https://data.velopark.be/openvelopark/terms#"
vpt: "https://data.velopark.be/openvelopark/terms#",
},
[url],
undefined,
@ -543,7 +541,7 @@ export default class LinkedDataLoader {
mv: "http://schema.mobivoc.org/",
gr: "http://purl.org/goodrelations/v1#",
vp: "https://data.velopark.be/openvelopark/vocabulary#",
vpt: "https://data.velopark.be/openvelopark/terms#"
vpt: "https://data.velopark.be/openvelopark/terms#",
},
[url],
"g",
@ -686,20 +684,20 @@ export default class LinkedDataLoader {
const withProxyUrl = Constants.linkedDataProxy.replace("{url}", encodeURIComponent(url))
const optionalPaths: Record<string, string | Record<string, string>> = {
"schema:interactionService": {
"schema:url": "website"
"schema:url": "website",
},
"mv:operatedBy": {
"gr:legalName": "operator"
"gr:legalName": "operator",
},
"schema:contactPoint": {
"schema:email": "email",
"schema:telephone": "phone"
"schema:telephone": "phone",
},
"schema:dateModified": "_last_edit_timestamp"
"schema:dateModified": "_last_edit_timestamp",
}
if (includeExtras) {
optionalPaths["schema:address"] = {
"schema:streetAddress": "addr"
"schema:streetAddress": "addr",
}
optionalPaths["schema:name"] = "name"
optionalPaths["schema:description"] = "description"
@ -717,19 +715,19 @@ export default class LinkedDataLoader {
"schema:geo": {
"schema:latitude": "latitude",
"schema:longitude": "longitude",
"schema:polygon": "shape"
"schema:polygon": "shape",
},
"schema:priceSpecification": {
"mv:freeOfCharge": "fee",
"schema:price": "charge"
}
"schema:price": "charge",
},
}
const extra = [
"schema:priceSpecification [ mv:dueForTime [ mv:timeStartValue ?chargeStart; mv:timeEndValue ?chargeEnd; mv:timeUnit ?timeUnit ] ]",
"vp:allows [vp:bicycleType <https://data.velopark.be/openvelopark/terms#CargoBicycle>; vp:bicyclesAmount ?capacityCargobike; vp:bicycleType ?cargoBikeType]",
"vp:allows [vp:bicycleType <https://data.velopark.be/openvelopark/terms#ElectricBicycle>; vp:bicyclesAmount ?capacityElectric; vp:bicycleType ?electricBikeType]",
"vp:allows [vp:bicycleType <https://data.velopark.be/openvelopark/terms#TandemBicycle>; vp:bicyclesAmount ?capacityTandem; vp:bicycleType ?tandemBikeType]"
"vp:allows [vp:bicycleType <https://data.velopark.be/openvelopark/terms#TandemBicycle>; vp:bicyclesAmount ?capacityTandem; vp:bicycleType ?tandemBikeType]",
]
const unpatched = await this.fetchEntry(

View file

@ -206,23 +206,29 @@ export default class FeatureReviews {
this.subjectUri = this.ConstructSubjectUri()
this.subjectUri.addCallbackAndRunD(async (sub) => {
const reviews = await MangroveReviews.getReviews({ sub })
console.log("Got reviews for", feature, reviews, sub)
this.addReviews(reviews.reviews, this._name.data)
}, [this._name])
this.subjectUri.addCallbackAndRunD(
async (sub) => {
const reviews = await MangroveReviews.getReviews({ sub })
console.log("Got reviews for", feature, reviews, sub)
this.addReviews(reviews.reviews, this._name.data)
},
[this._name]
)
/* We also construct all subject queries _without_ encoding the name to work around a previous bug
* See https://github.com/giggls/opencampsitemap/issues/30
*/
this.ConstructSubjectUri(true).mapD(async (sub) => {
try {
const reviews = await MangroveReviews.getReviews({ sub })
console.log("Got reviews (no-encode) for", feature, reviews, sub)
this.addReviews(reviews.reviews, this._name.data)
} catch (e) {
console.log("Could not fetch reviews for partially incorrect query ", sub)
}
}, [this._name])
this.ConstructSubjectUri(true).mapD(
async (sub) => {
try {
const reviews = await MangroveReviews.getReviews({ sub })
console.log("Got reviews (no-encode) for", feature, reviews, sub)
this.addReviews(reviews.reviews, this._name.data)
} catch (e) {
console.log("Could not fetch reviews for partially incorrect query ", sub)
}
},
[this._name]
)
this.average = this._reviews.map((reviews) => {
if (!reviews) {
return null
@ -321,7 +327,10 @@ export default class FeatureReviews {
* @param reviews
* @private
*/
private addReviews(reviews: { payload: Review; kid: string; signature: string }[], expectedName: string) {
private addReviews(
reviews: { payload: Review; kid: string; signature: string }[],
expectedName: string
) {
const alreadyKnown = new Set(this._reviews.data.map((r) => r.rating + " " + r.opinion))
let hasNew = false
@ -333,20 +342,17 @@ export default class FeatureReviews {
if (url.protocol !== "geo:") {
continue
}
const coordinate = <[number, number]>(
url.pathname.split(",").map((n) => Number(n))
)
const distance = GeoOperations.distanceBetween(
[this._lat, this._lon],
coordinate
)
const coordinate = <[number, number]>url.pathname.split(",").map((n) => Number(n))
const distance = GeoOperations.distanceBetween([this._lat, this._lon], coordinate)
if (distance > this._uncertainty) {
continue
}
const nameUrl = url.searchParams.get("q")
const distanceName = Utils.levenshteinDistance(nameUrl.toLowerCase(), expectedName.toLowerCase()) / expectedName.length
const nameUrl = url.searchParams.get("q")
const distanceName =
Utils.levenshteinDistance(nameUrl.toLowerCase(), expectedName.toLowerCase()) /
expectedName.length
if(distanceName > 0.25){
if (distanceName > 0.25) {
// Then name is wildly different
continue
}
@ -356,7 +362,6 @@ export default class FeatureReviews {
continue
}
const key = review.rating + " " + review.opinion
if (alreadyKnown.has(key)) {
continue