forked from MapComplete/MapComplete
Chore: linting
This commit is contained in:
parent
4625ad9a5c
commit
097141f944
307 changed files with 5346 additions and 2147 deletions
|
|
@ -113,7 +113,7 @@ export default class GeoLocationHandler {
|
|||
* - The GPS-location iss NULL-island
|
||||
* @constructor
|
||||
*/
|
||||
public MoveMapToCurrentLocation(zoomToAtLeast: number = 14 ) {
|
||||
public MoveMapToCurrentLocation(zoomToAtLeast: number = 14) {
|
||||
const newLocation = this.geolocationState.currentGPSLocation.data
|
||||
const mapLocation = this.mapProperties.location
|
||||
// We got a new location.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,11 @@ import { Feature } from "geojson"
|
|||
import { ImageUploadManager } from "../ImageProviders/ImageUploadManager"
|
||||
|
||||
export default class PendingChangesUploader {
|
||||
constructor(changes: Changes, selectedFeature: UIEventSource<Feature>, uploader : ImageUploadManager) {
|
||||
constructor(
|
||||
changes: Changes,
|
||||
selectedFeature: UIEventSource<Feature>,
|
||||
uploader: ImageUploadManager
|
||||
) {
|
||||
changes.pendingChanges
|
||||
.stabilized(Constants.updateTimeoutSec * 1000)
|
||||
.addCallback(() => changes.flushChanges("Flushing changes due to timeout"))
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ export default class FeaturePropertiesStore {
|
|||
if (newId === undefined) {
|
||||
// We removed the node/way/relation with type 'type' and id 'oldId' on openstreetmap!
|
||||
const element = this._elements.get(oldId)
|
||||
if(!element || element.data === undefined){
|
||||
if (!element || element.data === undefined) {
|
||||
return
|
||||
}
|
||||
element.data._deleted = "yes"
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ export default class GeoJsonSource implements FeatureSource {
|
|||
const url = this.url
|
||||
try {
|
||||
const cacheAge = (options?.maxCacheAgeSec ?? 300) * 1000
|
||||
let json = <{features: Feature[]}> await Utils.downloadJsonCached(url, cacheAge)
|
||||
let json = <{ features: Feature[] }>await Utils.downloadJsonCached(url, cacheAge)
|
||||
|
||||
if (json.features === undefined || json.features === null) {
|
||||
json.features = []
|
||||
|
|
|
|||
|
|
@ -32,8 +32,10 @@ export interface SnappingOptions {
|
|||
reusePointWithin?: number
|
||||
}
|
||||
|
||||
export default class SnappingFeatureSource implements FeatureSource<Feature<Point, { "snapped-to": string; dist: number }>> {
|
||||
public readonly features: Store<[Feature<Point, { "snapped-to": string; dist: number }>]>
|
||||
export default class SnappingFeatureSource
|
||||
implements FeatureSource<Feature<Point, { "snapped-to": string; dist: number }>>
|
||||
{
|
||||
public readonly features: Store<[Feature<Point, { "snapped-to": string; dist: number }>]>
|
||||
/*Contains the id of the way it snapped to*/
|
||||
public readonly snappedTo: Store<string>
|
||||
private readonly _snappedTo: UIEventSource<string>
|
||||
|
|
|
|||
|
|
@ -817,7 +817,7 @@ export class GeoOperations {
|
|||
}
|
||||
return undefined
|
||||
default:
|
||||
throw "Unkown location type: " + location+" for feature "+feature.properties.id
|
||||
throw "Unkown location type: " + location + " for feature " + feature.properties.id
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,9 +42,12 @@ export class ImageUploadManager {
|
|||
const failed = this.getCounterFor(this._uploadFailed, "*")
|
||||
const done = this.getCounterFor(this._uploadFinished, "*")
|
||||
|
||||
this.isUploading = this.getCounterFor(this._uploadStarted, "*").map(startedCount => {
|
||||
return startedCount > failed.data + done.data
|
||||
}, [failed, done])
|
||||
this.isUploading = this.getCounterFor(this._uploadStarted, "*").map(
|
||||
(startedCount) => {
|
||||
return startedCount > failed.data + done.data
|
||||
},
|
||||
[failed, done]
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -92,9 +92,9 @@ export class Imgur extends ImageProvider implements ImageUploader {
|
|||
*
|
||||
*
|
||||
*/
|
||||
public async DownloadAttribution(providedImage: {url: string}): Promise<LicenseInfo> {
|
||||
public async DownloadAttribution(providedImage: { url: string }): Promise<LicenseInfo> {
|
||||
const url = providedImage.url
|
||||
const hash = url.substr("https://i.imgur.com/".length).split(/\.jpe?g/i)[0]
|
||||
const hash = url.substr("https://i.imgur.com/".length).split(/\.jpe?g/i)[0]
|
||||
|
||||
const apiUrl = "https://api.imgur.com/3/image/" + hash
|
||||
const response = await Utils.downloadJsonCached(apiUrl, 365 * 24 * 60 * 60, {
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ export class Mapillary extends ImageProvider {
|
|||
const url_hd = <string>response["thumb_original_url"]
|
||||
const date = new Date()
|
||||
date.setTime(response["captured_at"])
|
||||
return <ProvidedImage> {
|
||||
return <ProvidedImage>{
|
||||
id: "" + mapillaryId,
|
||||
url,
|
||||
url_hd,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export class WikimediaImageProvider extends ImageProvider {
|
|||
public static readonly singleton = new WikimediaImageProvider()
|
||||
public static readonly apiUrls = [
|
||||
"https://commons.wikimedia.org/wiki/",
|
||||
"https://upload.wikimedia.org"
|
||||
"https://upload.wikimedia.org",
|
||||
]
|
||||
public static readonly commonsPrefixes = [...WikimediaImageProvider.apiUrls, "File:"]
|
||||
private readonly commons_key = "wikimedia_commons"
|
||||
|
|
@ -42,7 +42,6 @@ export class WikimediaImageProvider extends ImageProvider {
|
|||
return baseUrl
|
||||
}
|
||||
return baseUrl + `?width=500&height=400`
|
||||
|
||||
}
|
||||
|
||||
private static startsWithCommonsPrefix(value: string): boolean {
|
||||
|
|
@ -174,7 +173,7 @@ export class WikimediaImageProvider extends ImageProvider {
|
|||
url_hd: WikimediaImageProvider.PrepareUrl(image, true),
|
||||
key: undefined,
|
||||
provider: this,
|
||||
id: image
|
||||
id: image,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ export class OsmConnection {
|
|||
oauth_token?: UIEventSource<string>
|
||||
// Used to keep multiple changesets open and to write to the correct changeset
|
||||
singlePage?: boolean
|
||||
attemptLogin?: true | boolean,
|
||||
attemptLogin?: true | boolean
|
||||
/**
|
||||
* If true: automatically check if we're still online every 5 minutes + fetch messages
|
||||
*/
|
||||
|
|
@ -119,7 +119,7 @@ export class OsmConnection {
|
|||
this._dryRun = options.dryRun ?? new UIEventSource<boolean>(false)
|
||||
|
||||
this.updateAuthObject()
|
||||
if(!this.fakeUser){
|
||||
if (!this.fakeUser) {
|
||||
self.CheckForMessagesContinuously()
|
||||
}
|
||||
|
||||
|
|
@ -202,9 +202,9 @@ export class OsmConnection {
|
|||
this.auth.xhr(
|
||||
{
|
||||
method: "GET",
|
||||
path: "/api/0.6/user/details"
|
||||
path: "/api/0.6/user/details",
|
||||
},
|
||||
function(err, details: XMLDocument) {
|
||||
function (err, details: XMLDocument) {
|
||||
if (err != null) {
|
||||
console.log("Could not login due to:", err)
|
||||
self.loadingStatus.setData("error")
|
||||
|
|
@ -313,12 +313,12 @@ export class OsmConnection {
|
|||
<any>{
|
||||
method,
|
||||
options: {
|
||||
header
|
||||
header,
|
||||
},
|
||||
content,
|
||||
path: `/api/0.6/${path}`
|
||||
path: `/api/0.6/${path}`,
|
||||
},
|
||||
function(err, response) {
|
||||
function (err, response) {
|
||||
if (err !== null) {
|
||||
error(err)
|
||||
} else {
|
||||
|
|
@ -398,7 +398,7 @@ export class OsmConnection {
|
|||
"notes.json",
|
||||
content,
|
||||
{
|
||||
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
|
||||
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
},
|
||||
true
|
||||
)
|
||||
|
|
@ -439,7 +439,7 @@ export class OsmConnection {
|
|||
file: gpx,
|
||||
description: options.description,
|
||||
tags: options.labels?.join(",") ?? "",
|
||||
visibility: options.visibility
|
||||
visibility: options.visibility,
|
||||
}
|
||||
|
||||
if (!contents.description) {
|
||||
|
|
@ -447,9 +447,9 @@ export class OsmConnection {
|
|||
}
|
||||
const extras = {
|
||||
file:
|
||||
"; filename=\"" +
|
||||
'; filename="' +
|
||||
(options.filename ?? "gpx_track_mapcomplete_" + new Date().toISOString()) +
|
||||
"\"\r\nContent-Type: application/gpx+xml"
|
||||
'"\r\nContent-Type: application/gpx+xml',
|
||||
}
|
||||
|
||||
const boundary = "987654"
|
||||
|
|
@ -457,7 +457,7 @@ export class OsmConnection {
|
|||
let body = ""
|
||||
for (const key in contents) {
|
||||
body += "--" + boundary + "\r\n"
|
||||
body += "Content-Disposition: form-data; name=\"" + key + "\""
|
||||
body += 'Content-Disposition: form-data; name="' + key + '"'
|
||||
if (extras[key] !== undefined) {
|
||||
body += extras[key]
|
||||
}
|
||||
|
|
@ -468,7 +468,7 @@ export class OsmConnection {
|
|||
|
||||
const response = await this.post("gpx/create", body, {
|
||||
"Content-Type": "multipart/form-data; boundary=" + boundary,
|
||||
"Content-Length": body.length
|
||||
"Content-Length": body.length,
|
||||
})
|
||||
const parsed = JSON.parse(response)
|
||||
console.log("Uploaded GPX track", parsed)
|
||||
|
|
@ -491,9 +491,9 @@ export class OsmConnection {
|
|||
{
|
||||
method: "POST",
|
||||
|
||||
path: `/api/0.6/notes/${id}/comment?text=${encodeURIComponent(text)}`
|
||||
path: `/api/0.6/notes/${id}/comment?text=${encodeURIComponent(text)}`,
|
||||
},
|
||||
function(err, _) {
|
||||
function (err, _) {
|
||||
if (err !== null) {
|
||||
error(err)
|
||||
} else {
|
||||
|
|
@ -508,7 +508,7 @@ export class OsmConnection {
|
|||
* To be called by land.html
|
||||
*/
|
||||
public finishLogin(callback: (previousURL: string) => void) {
|
||||
this.auth.authenticate(function() {
|
||||
this.auth.authenticate(function () {
|
||||
// Fully authed at this point
|
||||
console.log("Authentication successful!")
|
||||
const previousLocation = LocalStorageSource.Get("location_before_login")
|
||||
|
|
@ -547,7 +547,7 @@ export class OsmConnection {
|
|||
? "https://mapcomplete.org/land.html"
|
||||
: window.location.protocol + "//" + window.location.host + "/land.html",
|
||||
singlepage: true,
|
||||
auto: true
|
||||
auto: true,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(/</g,'<')?.replace(/>/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(/</g, "<")?.replace(/>/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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -243,10 +243,7 @@ export class TagUtils {
|
|||
*
|
||||
* TagUtils.SplitKeysRegex([new Tag("isced:level", "bachelor; master")], true) // => {"isced:level": ["bachelor","master"]}
|
||||
*/
|
||||
static SplitKeysRegex(
|
||||
tagsFilters: UploadableTag[],
|
||||
allowRegex: false
|
||||
): Record<string, string[]>
|
||||
static SplitKeysRegex(tagsFilters: UploadableTag[], allowRegex: false): Record<string, string[]>
|
||||
static SplitKeysRegex(
|
||||
tagsFilters: UploadableTag[],
|
||||
allowRegex: boolean
|
||||
|
|
@ -514,7 +511,7 @@ export class TagUtils {
|
|||
)}`
|
||||
})
|
||||
|
||||
return <UploadableTag> t
|
||||
return <UploadableTag>t
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -723,7 +720,9 @@ export class TagUtils {
|
|||
}
|
||||
if (typeof json != "string") {
|
||||
if (json["and"] !== undefined && json["or"] !== undefined) {
|
||||
throw `${context}: Error while parsing a TagConfig: got an object where both 'and' and 'or' are defined. Did you override a value? Perhaps use \`"=parent": { ... }\` instead of \"parent": {...}\` to trigger a replacement and not a fuse of values. The value is ${JSON.stringify(json)}`
|
||||
throw `${context}: Error while parsing a TagConfig: got an object where both 'and' and 'or' are defined. Did you override a value? Perhaps use \`"=parent": { ... }\` instead of \"parent": {...}\` to trigger a replacement and not a fuse of values. The value is ${JSON.stringify(
|
||||
json
|
||||
)}`
|
||||
}
|
||||
if (json["and"] !== undefined) {
|
||||
return new And(json["and"].map((t) => TagUtils.Tag(t, context)))
|
||||
|
|
|
|||
|
|
@ -237,10 +237,10 @@ export abstract class Store<T> implements Readable<T> {
|
|||
|
||||
public bindD<X>(f: (t: Exclude<T, undefined | null>) => Store<X>): Store<X> {
|
||||
return this.bind((t) => {
|
||||
if(t=== null){
|
||||
if (t === null) {
|
||||
return null
|
||||
}
|
||||
if (t === undefined ) {
|
||||
if (t === undefined) {
|
||||
return undefined
|
||||
}
|
||||
return f(<Exclude<T, undefined | null>>t)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ interface JsonLdLoaderOptions {
|
|||
country?: string
|
||||
}
|
||||
|
||||
type PropertiesSpec<T extends string> = Partial<Record<T, string | string[] | Partial<Record<T, string>>>>
|
||||
type PropertiesSpec<T extends string> = Partial<
|
||||
Record<T, string | string[] | Partial<Record<T, string>>>
|
||||
>
|
||||
|
||||
export default class LinkedDataLoader {
|
||||
private static readonly COMPACTING_CONTEXT = {
|
||||
|
|
@ -25,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",
|
||||
|
|
@ -54,53 +56,61 @@ 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 {
|
||||
const polygon = str.substring("POLYGON ((".length, str.length - 2)
|
||||
return <Polygon>{
|
||||
type: "Polygon",
|
||||
coordinates: [polygon.split(",").map(coors => coors.trim().split(" ").map(n => Number(n)))]
|
||||
coordinates: [
|
||||
polygon.split(",").map((coors) =>
|
||||
coors
|
||||
.trim()
|
||||
.split(" ")
|
||||
.map((n) => Number(n))
|
||||
),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
private static async geoToGeometry(geo): Promise<Geometry> {
|
||||
if (Array.isArray(geo)) {
|
||||
const features = await Promise.all(geo.map(g => LinkedDataLoader.geoToGeometry(g)))
|
||||
const polygon = features.find(f => f.type === "Polygon")
|
||||
const features = await Promise.all(geo.map((g) => LinkedDataLoader.geoToGeometry(g)))
|
||||
const polygon = features.find((f) => f.type === "Polygon")
|
||||
if (polygon) {
|
||||
return polygon
|
||||
}
|
||||
const ls = features.find(f => f.type === "LineString")
|
||||
const ls = features.find((f) => f.type === "LineString")
|
||||
if (ls) {
|
||||
return ls
|
||||
}
|
||||
return features[0]
|
||||
|
||||
}
|
||||
|
||||
if (geo["@type"] === "http://schema.org/GeoCoordinates") {
|
||||
|
||||
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)],
|
||||
}
|
||||
}
|
||||
|
||||
if (geo["@type"] === "http://schema.org/GeoShape" && geo["http://schema.org/polygon"] !== undefined) {
|
||||
if (
|
||||
geo["@type"] === "http://schema.org/GeoShape" &&
|
||||
geo["http://schema.org/polygon"] !== undefined
|
||||
) {
|
||||
const str = geo["http://schema.org/polygon"]["@value"]
|
||||
LinkedDataLoader.shapeToPolygon(str)
|
||||
}
|
||||
|
|
@ -167,9 +177,8 @@ export default class LinkedDataLoader {
|
|||
}
|
||||
|
||||
static async compact(data: object, options?: JsonLdLoaderOptions): Promise<object> {
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
return await Promise.all(data.map(point => LinkedDataLoader.compact(point, options)))
|
||||
return await Promise.all(data.map((point) => LinkedDataLoader.compact(point, options)))
|
||||
}
|
||||
|
||||
const country = options?.country
|
||||
|
|
@ -214,10 +223,13 @@ export default class LinkedDataLoader {
|
|||
}
|
||||
}
|
||||
return compacted
|
||||
|
||||
}
|
||||
|
||||
static async fetchJsonLd(url: string, options?: JsonLdLoaderOptions, useProxy: boolean = false): Promise<object> {
|
||||
static async fetchJsonLd(
|
||||
url: string,
|
||||
options?: JsonLdLoaderOptions,
|
||||
useProxy: boolean = false
|
||||
): Promise<object> {
|
||||
if (useProxy) {
|
||||
url = Constants.linkedDataProxy.replace("{url}", encodeURIComponent(url))
|
||||
}
|
||||
|
|
@ -230,7 +242,10 @@ export default class LinkedDataLoader {
|
|||
* @param externalData
|
||||
* @param currentData
|
||||
*/
|
||||
static removeDuplicateData(externalData: Record<string, string>, currentData: Record<string, string>): Record<string, string> {
|
||||
static removeDuplicateData(
|
||||
externalData: Record<string, string>,
|
||||
currentData: Record<string, string>
|
||||
): Record<string, string> {
|
||||
const d = { ...externalData }
|
||||
delete d["@context"]
|
||||
for (const k in d) {
|
||||
|
|
@ -239,7 +254,7 @@ export default class LinkedDataLoader {
|
|||
continue
|
||||
}
|
||||
if (k === "opening_hours") {
|
||||
const oh = [].concat(...v.split(";").map(r => OH.ParseRule(r) ?? []))
|
||||
const oh = [].concat(...v.split(";").map((r) => OH.ParseRule(r) ?? []))
|
||||
const merged = OH.ToString(OH.MergeTimes(oh ?? []))
|
||||
if (merged === d[k]) {
|
||||
delete d[k]
|
||||
|
|
@ -259,7 +274,9 @@ export default class LinkedDataLoader {
|
|||
const properties: Record<string, string> = {}
|
||||
for (const k in linkedData) {
|
||||
if (linkedData[k].length > 1) {
|
||||
throw "Found multiple values in properties for " + k + ": " + linkedData[k].join("; ")
|
||||
throw (
|
||||
"Found multiple values in properties for " + k + ": " + linkedData[k].join("; ")
|
||||
)
|
||||
}
|
||||
properties[k] = linkedData[k].join("; ")
|
||||
}
|
||||
|
|
@ -268,7 +285,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"]
|
||||
|
|
@ -277,11 +294,10 @@ export default class LinkedDataLoader {
|
|||
geometry = LinkedDataLoader.shapeToPolygon(properties["shape"])
|
||||
}
|
||||
|
||||
|
||||
const geo: GeoJSON = {
|
||||
type: "Feature",
|
||||
properties,
|
||||
geometry
|
||||
geometry,
|
||||
}
|
||||
delete linkedData.geo
|
||||
delete properties.shape
|
||||
|
|
@ -293,7 +309,9 @@ export default class LinkedDataLoader {
|
|||
return geo
|
||||
}
|
||||
|
||||
private static patchVeloparkProperties(input: Record<string, Set<string>>): Record<string, string[]> {
|
||||
private static patchVeloparkProperties(
|
||||
input: Record<string, Set<string>>
|
||||
): Record<string, string[]> {
|
||||
const output: Record<string, string[]> = {}
|
||||
console.log("Input for patchVelopark:", input)
|
||||
for (const k in input) {
|
||||
|
|
@ -309,12 +327,12 @@ export default class LinkedDataLoader {
|
|||
if (!output[key]) {
|
||||
return
|
||||
}
|
||||
output[key] = output[key].map(v => applyF(v))
|
||||
output[key] = output[key].map((v) => applyF(v))
|
||||
}
|
||||
|
||||
function asBoolean(key: string, invert: boolean = false) {
|
||||
on(key, str => {
|
||||
const isTrue = ("" + str) === "true" || str === "True" || str === "yes"
|
||||
on(key, (str) => {
|
||||
const isTrue = "" + str === "true" || str === "True" || str === "yes"
|
||||
if (isTrue != invert) {
|
||||
return "yes"
|
||||
}
|
||||
|
|
@ -322,7 +340,7 @@ export default class LinkedDataLoader {
|
|||
})
|
||||
}
|
||||
|
||||
on("maxstay", (maxstay => {
|
||||
on("maxstay", (maxstay) => {
|
||||
const match = maxstay.match(/P([0-9]+)D/)
|
||||
if (match) {
|
||||
const days = Number(match[1])
|
||||
|
|
@ -332,7 +350,7 @@ export default class LinkedDataLoader {
|
|||
return days + " days"
|
||||
}
|
||||
return maxstay
|
||||
}))
|
||||
})
|
||||
|
||||
function rename(source: string, target: string) {
|
||||
if (output[source] === undefined || output[source] === null) {
|
||||
|
|
@ -342,41 +360,40 @@ export default class LinkedDataLoader {
|
|||
delete output[source]
|
||||
}
|
||||
|
||||
on("phone", (p => this.formatters["phone"].reformat(p, () => "be")))
|
||||
on("phone", (p) => this.formatters["phone"].reformat(p, () => "be"))
|
||||
|
||||
for (const attribute in LinkedDataLoader.formatters) {
|
||||
on(attribute, p => LinkedDataLoader.formatters[attribute].reformat(p))
|
||||
on(attribute, (p) => LinkedDataLoader.formatters[attribute].reformat(p))
|
||||
}
|
||||
rename("phone", "operator:phone")
|
||||
rename("email", "operator:email")
|
||||
rename("website", "operator:website")
|
||||
|
||||
on("charge", (p => {
|
||||
on("charge", (p) => {
|
||||
if (Number(p) === 0) {
|
||||
output["fee"] = ["no"]
|
||||
return undefined
|
||||
}
|
||||
return "€" + Number(p)
|
||||
}))
|
||||
})
|
||||
if (output["charge"] && output["timeUnit"]) {
|
||||
const duration = Number(output["chargeEnd"] ?? "1") - Number(output["chargeStart"] ?? "0")
|
||||
const duration =
|
||||
Number(output["chargeEnd"] ?? "1") - Number(output["chargeStart"] ?? "0")
|
||||
const unit = output["timeUnit"][0]
|
||||
let durationStr = ""
|
||||
if (duration !== 1) {
|
||||
durationStr = duration + ""
|
||||
}
|
||||
output["charge"] = output["charge"].map(c => c + "/" + (durationStr + unit))
|
||||
output["charge"] = output["charge"].map((c) => c + "/" + (durationStr + unit))
|
||||
}
|
||||
delete output["chargeEnd"]
|
||||
delete output["chargeStart"]
|
||||
delete output["timeUnit"]
|
||||
|
||||
|
||||
asBoolean("covered")
|
||||
asBoolean("fee", true)
|
||||
asBoolean("publicAccess")
|
||||
|
||||
|
||||
output["images"]?.forEach((p, i) => {
|
||||
if (i === 0) {
|
||||
output["image"] = [p]
|
||||
|
|
@ -386,9 +403,15 @@ export default class LinkedDataLoader {
|
|||
})
|
||||
delete output["images"]
|
||||
|
||||
on("access", audience => {
|
||||
|
||||
if (["brede publiek", "iedereen", "bezoekers", "iedereen - vooral bezoekers gemeentehuis of bibliotheek."].indexOf(audience.toLowerCase()) >= 0) {
|
||||
on("access", (audience) => {
|
||||
if (
|
||||
[
|
||||
"brede publiek",
|
||||
"iedereen",
|
||||
"bezoekers",
|
||||
"iedereen - vooral bezoekers gemeentehuis of bibliotheek.",
|
||||
].indexOf(audience.toLowerCase()) >= 0
|
||||
) {
|
||||
return "yes"
|
||||
}
|
||||
if (audience.toLowerCase().startsWith("bezoekers")) {
|
||||
|
|
@ -404,15 +427,22 @@ export default class LinkedDataLoader {
|
|||
return "permissive"
|
||||
// return "members"
|
||||
}
|
||||
if (audience.toLowerCase().startsWith("klanten") ||
|
||||
if (
|
||||
audience.toLowerCase().startsWith("klanten") ||
|
||||
audience.toLowerCase().startsWith("werknemers") ||
|
||||
audience.toLowerCase().startsWith("personeel")) {
|
||||
audience.toLowerCase().startsWith("personeel")
|
||||
) {
|
||||
return "customers"
|
||||
}
|
||||
|
||||
console.warn("Suspicious 'access'-tag:", audience, "for", input["ref:velopark"], " assuming yes")
|
||||
console.warn(
|
||||
"Suspicious 'access'-tag:",
|
||||
audience,
|
||||
"for",
|
||||
input["ref:velopark"],
|
||||
" assuming yes"
|
||||
)
|
||||
return "yes"
|
||||
|
||||
})
|
||||
|
||||
if (output["publicAccess"]?.[0] == "no") {
|
||||
|
|
@ -420,7 +450,10 @@ export default class LinkedDataLoader {
|
|||
}
|
||||
delete output["publicAccess"]
|
||||
|
||||
if (output["restrictions"]?.[0] === "Geen bromfietsen, noch andere gemotoriseerde voertuigen") {
|
||||
if (
|
||||
output["restrictions"]?.[0] ===
|
||||
"Geen bromfietsen, noch andere gemotoriseerde voertuigen"
|
||||
) {
|
||||
output["motor_vehicle"] = ["no"]
|
||||
delete output["restrictions"]
|
||||
}
|
||||
|
|
@ -437,7 +470,6 @@ export default class LinkedDataLoader {
|
|||
}
|
||||
rename("capacityTandem", "capacity:tandem")
|
||||
|
||||
|
||||
if (output["electricBikeType"]) {
|
||||
output["electric_bicycle"] = ["yes"]
|
||||
delete output["electricBikeType"]
|
||||
|
|
@ -450,14 +482,18 @@ export default class LinkedDataLoader {
|
|||
return output
|
||||
}
|
||||
|
||||
private static async fetchVeloparkProperty<T extends string, G extends T>(url: string, property: string, variable?: string): Promise<SparqlResult<T, G>> {
|
||||
private static async fetchVeloparkProperty<T extends string, G extends T>(
|
||||
url: string,
|
||||
property: string,
|
||||
variable?: string
|
||||
): Promise<SparqlResult<T, G>> {
|
||||
const results = await new TypedSparql().typedSparql<T, G>(
|
||||
{
|
||||
schema: "http://schema.org/",
|
||||
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,
|
||||
|
|
@ -467,24 +503,24 @@ export default class LinkedDataLoader {
|
|||
return results
|
||||
}
|
||||
|
||||
private static async fetchVeloparkGraphProperty<T extends string>(url: string, property: string, subExpr?: string):
|
||||
Promise<SparqlResult<T, "g">> {
|
||||
private static async fetchVeloparkGraphProperty<T extends string>(
|
||||
url: string,
|
||||
property: string,
|
||||
subExpr?: string
|
||||
): Promise<SparqlResult<T, "g">> {
|
||||
return await new TypedSparql().typedSparql<T, "g">(
|
||||
{
|
||||
schema: "http://schema.org/",
|
||||
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",
|
||||
" ?parking a <http://schema.mobivoc.org/BicycleParkingStation>",
|
||||
|
||||
S.graph("g",
|
||||
"?section " + property + " " + (subExpr ?? ""),
|
||||
"?section a ?type"
|
||||
)
|
||||
S.graph("g", "?section " + property + " " + (subExpr ?? ""), "?section a ?type")
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -493,8 +529,10 @@ export default class LinkedDataLoader {
|
|||
* THis is a workaround for 'optional' not working decently
|
||||
* @param r0
|
||||
*/
|
||||
public static mergeResults(...r0: SparqlResult<string, string>[]): SparqlResult<string, string> {
|
||||
const r: SparqlResult<string> = { "default": {} }
|
||||
public static mergeResults(
|
||||
...r0: SparqlResult<string, string>[]
|
||||
): SparqlResult<string, string> {
|
||||
const r: SparqlResult<string> = { default: {} }
|
||||
for (const subResult of r0) {
|
||||
if (Object.keys(subResult).length === 0) {
|
||||
continue
|
||||
|
|
@ -524,22 +562,31 @@ export default class LinkedDataLoader {
|
|||
return r
|
||||
}
|
||||
|
||||
public static async fetchEntry<T extends string>(directUrl: string,
|
||||
propertiesWithoutGraph: PropertiesSpec<T>,
|
||||
propertiesInGraph: PropertiesSpec<T>,
|
||||
extra?: string[]): Promise<SparqlResult<T, string>> {
|
||||
public static async fetchEntry<T extends string>(
|
||||
directUrl: string,
|
||||
propertiesWithoutGraph: PropertiesSpec<T>,
|
||||
propertiesInGraph: PropertiesSpec<T>,
|
||||
extra?: string[]
|
||||
): Promise<SparqlResult<T, string>> {
|
||||
const allPartialResults: SparqlResult<T, string>[] = []
|
||||
for (const propertyName in propertiesWithoutGraph) {
|
||||
const e = propertiesWithoutGraph[propertyName]
|
||||
if (typeof e === "string") {
|
||||
const variableName = e
|
||||
const result = await this.fetchVeloparkProperty(directUrl, propertyName, "?" + variableName)
|
||||
const result = await this.fetchVeloparkProperty(
|
||||
directUrl,
|
||||
propertyName,
|
||||
"?" + variableName
|
||||
)
|
||||
allPartialResults.push(result)
|
||||
} else {
|
||||
for (const subProperty in e) {
|
||||
const variableName = e[subProperty]
|
||||
const result = await this.fetchVeloparkProperty(directUrl,
|
||||
propertyName, `[${subProperty} ?${variableName}] `)
|
||||
const result = await this.fetchVeloparkProperty(
|
||||
directUrl,
|
||||
propertyName,
|
||||
`[${subProperty} ?${variableName}] `
|
||||
)
|
||||
allPartialResults.push(result)
|
||||
}
|
||||
}
|
||||
|
|
@ -553,7 +600,11 @@ export default class LinkedDataLoader {
|
|||
if (variableName.match(/[a-zA-Z_]+/)) {
|
||||
variableName = "?" + subquery
|
||||
}
|
||||
const result = await this.fetchVeloparkGraphProperty(directUrl, propertyName, variableName)
|
||||
const result = await this.fetchVeloparkGraphProperty(
|
||||
directUrl,
|
||||
propertyName,
|
||||
variableName
|
||||
)
|
||||
allPartialResults.push(result)
|
||||
}
|
||||
} else if (typeof e === "string") {
|
||||
|
|
@ -561,13 +612,20 @@ export default class LinkedDataLoader {
|
|||
if (variableName.match(/[a-zA-Z_]+/)) {
|
||||
variableName = "?" + e
|
||||
}
|
||||
const result = await this.fetchVeloparkGraphProperty(directUrl, propertyName, variableName)
|
||||
const result = await this.fetchVeloparkGraphProperty(
|
||||
directUrl,
|
||||
propertyName,
|
||||
variableName
|
||||
)
|
||||
allPartialResults.push(result)
|
||||
} else {
|
||||
for (const subProperty in e) {
|
||||
const variableName = e[subProperty]
|
||||
const result = await this.fetchVeloparkGraphProperty(directUrl,
|
||||
propertyName, `[${subProperty} ?${variableName}] `)
|
||||
const result = await this.fetchVeloparkGraphProperty(
|
||||
directUrl,
|
||||
propertyName,
|
||||
`[${subProperty} ?${variableName}] `
|
||||
)
|
||||
allPartialResults.push(result)
|
||||
}
|
||||
}
|
||||
|
|
@ -581,7 +639,6 @@ export default class LinkedDataLoader {
|
|||
const results = this.mergeResults(...allPartialResults)
|
||||
|
||||
return results
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -593,22 +650,21 @@ 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",
|
||||
},
|
||||
"schema:name": "name",
|
||||
"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",
|
||||
}
|
||||
|
||||
const graphOptionalPaths = {
|
||||
"a": "type",
|
||||
a: "type",
|
||||
"vp:covered": "covered",
|
||||
"vp:maximumParkingDuration": "maxstay",
|
||||
"mv:totalCapacity": "capacity",
|
||||
|
|
@ -619,22 +675,27 @@ 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(withProxyUrl, optionalPaths, graphOptionalPaths, extra)
|
||||
const unpatched = await this.fetchEntry(
|
||||
withProxyUrl,
|
||||
optionalPaths,
|
||||
graphOptionalPaths,
|
||||
extra
|
||||
)
|
||||
const patched: Feature[] = []
|
||||
for (const section in unpatched) {
|
||||
const p = LinkedDataLoader.patchVeloparkProperties(unpatched[section])
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import { GeoOperations } from "../GeoOperations"
|
|||
import ScriptUtils from "../../../scripts/ScriptUtils"
|
||||
|
||||
export class MangroveIdentity {
|
||||
private readonly keypair: UIEventSource<CryptoKeyPair> = new UIEventSource<CryptoKeyPair>(undefined)
|
||||
private readonly keypair: UIEventSource<CryptoKeyPair> = new UIEventSource<CryptoKeyPair>(
|
||||
undefined
|
||||
)
|
||||
/**
|
||||
* Same as the one in the user settings
|
||||
*/
|
||||
|
|
@ -14,16 +16,19 @@ export class MangroveIdentity {
|
|||
private readonly key_id: UIEventSource<string> = new UIEventSource<string>(undefined)
|
||||
private readonly _mangroveIdentityCreationDate: UIEventSource<string>
|
||||
|
||||
constructor(mangroveIdentity: UIEventSource<string>, mangroveIdentityCreationDate: UIEventSource<string>) {
|
||||
constructor(
|
||||
mangroveIdentity: UIEventSource<string>,
|
||||
mangroveIdentityCreationDate: UIEventSource<string>
|
||||
) {
|
||||
this.mangroveIdentity = mangroveIdentity
|
||||
this._mangroveIdentityCreationDate = mangroveIdentityCreationDate
|
||||
mangroveIdentity.addCallbackAndRunD(async (data) => {
|
||||
await this.setKeypair(data)
|
||||
await this.setKeypair(data)
|
||||
})
|
||||
}
|
||||
|
||||
private async setKeypair(data: string){
|
||||
console.log("Setting keypair from",data)
|
||||
private async setKeypair(data: string) {
|
||||
console.log("Setting keypair from", data)
|
||||
const keypair = await MangroveReviews.jwkToKeypair(JSON.parse(data))
|
||||
this.keypair.setData(keypair)
|
||||
const pem = await MangroveReviews.publicToPem(keypair.publicKey)
|
||||
|
|
@ -71,22 +76,21 @@ export class MangroveIdentity {
|
|||
return this.key_id
|
||||
}
|
||||
|
||||
private geoReviewsById: Store<(Review & { kid: string; signature: string })[]> =
|
||||
undefined
|
||||
private geoReviewsById: Store<(Review & { kid: string; signature: string })[]> = undefined
|
||||
|
||||
public getGeoReviews(): Store<(Review & { kid: string, signature: string })[] | undefined> {
|
||||
public getGeoReviews(): Store<(Review & { kid: string; signature: string })[] | undefined> {
|
||||
if (!this.geoReviewsById) {
|
||||
const all = this.getAllReviews()
|
||||
this.geoReviewsById = this.getAllReviews().mapD(reviews => reviews.filter(
|
||||
review => {
|
||||
this.geoReviewsById = this.getAllReviews().mapD((reviews) =>
|
||||
reviews.filter((review) => {
|
||||
try {
|
||||
const subjectUrl = new URL(review.sub)
|
||||
return subjectUrl.protocol === "geo:"
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
))
|
||||
})
|
||||
)
|
||||
}
|
||||
return this.geoReviewsById
|
||||
}
|
||||
|
|
@ -108,12 +112,12 @@ export class MangroveIdentity {
|
|||
return []
|
||||
}
|
||||
const allReviews = await MangroveReviews.getReviews({
|
||||
kid: pem
|
||||
kid: pem,
|
||||
})
|
||||
this.allReviewsById.setData(
|
||||
allReviews.reviews.map((r) => ({
|
||||
...r,
|
||||
...r.payload
|
||||
...r.payload,
|
||||
}))
|
||||
)
|
||||
})
|
||||
|
|
@ -247,7 +251,13 @@ export default class FeatureReviews {
|
|||
if (cached !== undefined) {
|
||||
return cached
|
||||
}
|
||||
const featureReviews = new FeatureReviews(feature, tagsSource, mangroveIdentity, options,testmode )
|
||||
const featureReviews = new FeatureReviews(
|
||||
feature,
|
||||
tagsSource,
|
||||
mangroveIdentity,
|
||||
options,
|
||||
testmode
|
||||
)
|
||||
FeatureReviews._featureReviewsCache[key] = featureReviews
|
||||
return featureReviews
|
||||
}
|
||||
|
|
@ -268,7 +278,7 @@ export default class FeatureReviews {
|
|||
}
|
||||
const r: Review = {
|
||||
sub: this.subjectUri.data,
|
||||
...review
|
||||
...review,
|
||||
}
|
||||
const keypair: CryptoKeyPair = await this._identity.getKeypair()
|
||||
const jwt = await MangroveReviews.signReview(keypair, r)
|
||||
|
|
@ -283,7 +293,7 @@ export default class FeatureReviews {
|
|||
...r,
|
||||
kid,
|
||||
signature: jwt,
|
||||
madeByLoggedInUser: new ImmutableStore(true)
|
||||
madeByLoggedInUser: new ImmutableStore(true),
|
||||
}
|
||||
this._reviews.data.push(reviewWithKid)
|
||||
this._reviews.ping()
|
||||
|
|
@ -331,7 +341,7 @@ export default class FeatureReviews {
|
|||
signature: reviewData.signature,
|
||||
madeByLoggedInUser: this._identity.getKeyId().map((user_key_id) => {
|
||||
return reviewData.kid === user_key_id
|
||||
})
|
||||
}),
|
||||
})
|
||||
hasNew = true
|
||||
}
|
||||
|
|
@ -352,7 +362,7 @@ export default class FeatureReviews {
|
|||
// https://www.rfc-editor.org/rfc/rfc5870#section-3.4.2
|
||||
// `u` stands for `uncertainty`, https://www.rfc-editor.org/rfc/rfc5870#section-3.4.3
|
||||
const self = this
|
||||
return this._name.map(function(name) {
|
||||
return this._name.map(function (name) {
|
||||
let uri = `geo:${self._lat},${self._lon}?u=${Math.round(self._uncertainty)}`
|
||||
if (name) {
|
||||
uri += "&q=" + (dontEncodeName ? name : encodeURIComponent(name))
|
||||
|
|
|
|||
|
|
@ -3,11 +3,16 @@ import { QueryEngine } from "@comunica/query-sparql"
|
|||
|
||||
export type SparqlVar<T extends string> = `?${T}`
|
||||
export type SparqlExpr = string
|
||||
export type SparqlStmt<T extends string> = `${SparqlVar<T> | SparqlExpr} ${SparqlVar<T> | SparqlExpr} ${SparqlVar<T> | SparqlExpr}`
|
||||
export type SparqlStmt<T extends string> = `${SparqlVar<T> | SparqlExpr} ${
|
||||
| SparqlVar<T>
|
||||
| SparqlExpr} ${SparqlVar<T> | SparqlExpr}`
|
||||
|
||||
export type TypedExpression<T extends string> = SparqlStmt<T> | string
|
||||
|
||||
export type SparqlResult<T extends string, G extends string = "default"> = Record<G, Record<T, Set<string>>>
|
||||
export type SparqlResult<T extends string, G extends string = "default"> = Record<
|
||||
G,
|
||||
Record<T, Set<string>>
|
||||
>
|
||||
|
||||
export default class TypedSparql {
|
||||
private readonly comunica: QueryEngine
|
||||
|
|
@ -16,15 +21,23 @@ export default class TypedSparql {
|
|||
this.comunica = new QueryEngine()
|
||||
}
|
||||
|
||||
public static optional<Vars extends string>(...statements: (TypedExpression<Vars> | string)[]): TypedExpression<Vars> {
|
||||
public static optional<Vars extends string>(
|
||||
...statements: (TypedExpression<Vars> | string)[]
|
||||
): TypedExpression<Vars> {
|
||||
return ` OPTIONAL { ${statements.join(". \n\t")} }`
|
||||
}
|
||||
|
||||
public static graph<Vars extends string>(varname: Vars, ...statements: (string | TypedExpression<Vars>)[]): TypedExpression<Vars> {
|
||||
public static graph<Vars extends string>(
|
||||
varname: Vars,
|
||||
...statements: (string | TypedExpression<Vars>)[]
|
||||
): TypedExpression<Vars> {
|
||||
return `GRAPH ?${varname} { ${statements.join(".\n")} }`
|
||||
}
|
||||
|
||||
public static about<Vars extends string>(varname: Vars, ...statements: `${SparqlVar<Vars> | SparqlExpr} ${SparqlVar<Vars> | SparqlExpr}`[]): TypedExpression<Vars> {
|
||||
public static about<Vars extends string>(
|
||||
varname: Vars,
|
||||
...statements: `${SparqlVar<Vars> | SparqlExpr} ${SparqlVar<Vars> | SparqlExpr}`[]
|
||||
): TypedExpression<Vars> {
|
||||
return `?${varname} ${statements.join(";")}`
|
||||
}
|
||||
|
||||
|
|
@ -43,23 +56,22 @@ export default class TypedSparql {
|
|||
): Promise<SparqlResult<VARS, G>> {
|
||||
const q: string = this.buildQuery(query, prefixes)
|
||||
try {
|
||||
const bindingsStream = await this.comunica.queryBindings(
|
||||
q, { sources: [...sources], lenient: true }
|
||||
)
|
||||
const bindingsStream = await this.comunica.queryBindings(q, {
|
||||
sources: [...sources],
|
||||
lenient: true,
|
||||
})
|
||||
const bindings = await bindingsStream.toArray()
|
||||
|
||||
const resultAllGraphs: SparqlResult<VARS, G> = <SparqlResult<VARS, G>>{}
|
||||
|
||||
bindings.forEach(item => {
|
||||
bindings.forEach((item) => {
|
||||
const result = <Record<VARS | G, Set<string>>>{}
|
||||
item.forEach(
|
||||
(value, key) => {
|
||||
if (!result[key.value]) {
|
||||
result[key.value] = new Set()
|
||||
}
|
||||
result[key.value].add(value.value)
|
||||
item.forEach((value, key) => {
|
||||
if (!result[key.value]) {
|
||||
result[key.value] = new Set()
|
||||
}
|
||||
)
|
||||
result[key.value].add(value.value)
|
||||
})
|
||||
if (graphVariable && result[graphVariable]?.size > 0) {
|
||||
const id = Array.from(result[graphVariable])?.[0] ?? "default"
|
||||
resultAllGraphs[id] = result
|
||||
|
|
@ -73,14 +85,13 @@ export default class TypedSparql {
|
|||
console.log("Running query failed. The query is", q)
|
||||
throw e
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private buildQuery(
|
||||
query: readonly string[],
|
||||
prefixes: Record<string, string>): string {
|
||||
private buildQuery(query: readonly string[], prefixes: Record<string, string>): string {
|
||||
return `
|
||||
${Object.keys(prefixes).map(prefix => `PREFIX ${prefix}: <${prefixes[prefix]}>`).join("\n")}
|
||||
${Object.keys(prefixes)
|
||||
.map((prefix) => `PREFIX ${prefix}: <${prefixes[prefix]}>`)
|
||||
.join("\n")}
|
||||
SELECT *
|
||||
WHERE {
|
||||
${query.join(". \n")} .
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue