Themes(benches): more work on integrating openbenches, polish a bit more

This commit is contained in:
Pieter Vander Vennet 2025-10-12 01:51:20 +02:00
parent 87823caabc
commit 11ab4965c6
6 changed files with 212 additions and 221 deletions

View file

@ -12,7 +12,35 @@ A perfect example of this is to setup such a challenge to e.g. import new points
## Preparing the data ## Preparing the data
Convert your source data into a geojson. Use *`tags`* as field where all the OSM-properties should go. Make sure to include all tags there. Convert your source data into a geojson. Use *`tags`* as field where all the OSM-properties should go. Make sure to include all tags that should be imported there and stringify it.
You have quite some freedom for the other tags. Make sure to add an *id* tag. Other tags (e.g. images, metadata) may go into the properties as well.
However, do _not_ add the generic tags for the layer to import into, to avoid that the maproulette challenges are loaded by the target layer.
```
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [1.234, 5.678]},
"properties": {
"id": ..., # Especially important when you want to rebuild the data
"image": ...,
"lastModifiedBy": ..., # from the source data, just to help
"tags": "foo=bar;name=xyz;image=...", # To be imported
}
}
]
}
```
Then, create a challenge on https://maproulette.org/admin/projects (or, if you want to test first, on https://staging.maproulette.org/)
Hint: MapRoulette has a button 'rebuild task', where you can first 'remove all incomplete tasks'. This is perfect to start over in case of small data errors. Hint: MapRoulette has a button 'rebuild task', where you can first 'remove all incomplete tasks'. This is perfect to start over in case of small data errors.
@ -127,35 +155,3 @@ The following example uses the calculated tags `_has_closeby_feature` and `_clos
The easiest way is to reuse a tagrendering from the [Maproulette-layer](./Docs/Layers/maproulette.md) (_not_ the `maproulette-challenge`-layer!), such as [`maproulette.mark_fixed`](./Docs/Layers/maproulette.md#markfixed),[`maproulette.mark_duplicate`](./Docs/Layers/maproulette.md#markduplicate),[`maproulette.mark_too_hard`](./Docs/Layers/maproulette.md#marktoohard). The easiest way is to reuse a tagrendering from the [Maproulette-layer](./Docs/Layers/maproulette.md) (_not_ the `maproulette-challenge`-layer!), such as [`maproulette.mark_fixed`](./Docs/Layers/maproulette.md#markfixed),[`maproulette.mark_duplicate`](./Docs/Layers/maproulette.md#markduplicate),[`maproulette.mark_too_hard`](./Docs/Layers/maproulette.md#marktoohard).
In the background, these use the special visualisation [`maproulette_set_status`](./Docs/SpecialRenderings.md#maproulettesetstatus) - which allows to apply different status codes or different messages/icons. In the background, these use the special visualisation [`maproulette_set_status`](./Docs/SpecialRenderings.md#maproulettesetstatus) - which allows to apply different status codes or different messages/icons.
## Creating a maproulette challenge
A challenge can be created on https://maproulette.org/admin/projects
This can be done with a geojson-file (or by other means).
MapRoulette works as a geojson-store with status fields added. As such, you have a bit of freedom in creating the data, but an **id** field is mandatory. A **name** tag is recommended
To setup a guided import, add a `tags`-field with tags formatted in such a way that they are compatible with the [import-button](./Docs/SpecialRenderings.md#specifying-which-tags-to-copy-or-add)
(The following example is not tested and might be wrong.)
```
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [1.234, 5.678]},
"properties": {
"id": ...
"tags": "foo=bar;name=xyz",
}
}
]
}
```

View file

@ -107,16 +107,15 @@
"=tagRenderings": [ "=tagRenderings": [
{ {
"id": "explanation", "id": "explanation",
"classes":"border-2 border-interactive low-interaction p-2 m-2",
"render": { "render": {
"en": "This is a bench that is known in Openbenches.org but might not exist in OpenStreetMap. If this bench still exists in the real world, you can add or link this information to OpenStreetMap with the tools below" "en": "This is a bench that is known in Openbenches.org but might not exist in OpenStreetMap. If this bench still exists in the real world, you can add or link this information to OpenStreetMap with the tools below"
} }
}, },
{
"id": "images",
"render": "<img src='{image}'/>"
},
{ {
"id": "see_on_openbenches", "id": "see_on_openbenches",
"classes": "text-lg button",
"icon": "./assets/themes/benches/openbencheslogo.svg",
"render": { "render": {
"special": { "special": {
"type": "link", "type": "link",
@ -127,6 +126,10 @@
} }
} }
}, },
{
"id": "images",
"render": "{image_carousel()}"
},
{ {
"id": "closeness-indicator", "id": "closeness-indicator",
"condition": "_has_closeby_feature=yes", "condition": "_has_closeby_feature=yes",

View file

@ -20,5 +20,15 @@
"https://github.com/streetcomplete/StreetComplete/blob/v25.0-beta1/res/graphics/authors.txt", "https://github.com/streetcomplete/StreetComplete/blob/v25.0-beta1/res/graphics/authors.txt",
"https://github.com/streetcomplete/StreetComplete/tree/v25.0-beta1/res/graphics/quest%20icons" "https://github.com/streetcomplete/StreetComplete/tree/v25.0-beta1/res/graphics/quest%20icons"
] ]
},
{
"path": "openbencheslogo.svg",
"license": "LOGO",
"authors": [
"Terence Eden"
],
"sources": [
"https://openbenches.org/images/openbencheslogo.svg"
]
} }
] ]

View file

@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800">
<circle cx="400" cy="400" r="400" fill="#11b0a3"/>
<circle cx="573" cy="275" r="106" fill="#070"/>
<path d="M153 345h256v77H153z" fill="#fff"/>
<path d="M191 376h177v20H191z" fill="#ffba00"/>
<path d="M28.7 507.6h735.7C706.5 748.5 539.5 771.1 409.2 792c-181.7-27.8-324-103.8-380.5-284.4z" fill="#8c5015"/>
<path d="M607 490c9-29 66 4 161-112 29-7 15 79 8 111z" fill="#070"/>
<path d="M259 197l19-7c-1 0-10-36 17-50l-10-18a55 55 0 0 0-29 45 95 95 0 0 0-49-10l2 20s39-3 50 20zM326 256l18 9c1-2 19-33 48-24l5-19c-21-7-39 0-52 11-6-24-26-41-27-42l-13 15c0 1 29 26 21 50z" fill="#000"/>
<path d="M768.6 244.3a398.7 398.7 0 0 0-85.8-127.1A398.7 398.7 0 0 0 400 0a397.5 397.5 0 0 0-282.8 117.2A398.7 398.7 0 0 0 0 400a397.5 397.5 0 0 0 117.2 282.8A398.7 398.7 0 0 0 400 800a397.5 397.5 0 0 0 282.8-117.2A398.7 398.7 0 0 0 800 400c0-54-10.6-106.4-31.4-155.7zM30.3 487.6A380.4 380.4 0 0 1 400 20c197.2 0 359.7 151 378.2 343.4a69.2 69.2 0 0 0-56.6 57.7 71 71 0 0 0-53.8 37.2 48.7 48.7 0 0 0-66.1 29.1h-20.5V381.8a110.5 110.5 0 0 0-12-220.2 110.5 110.5 0 0 0-8.1 220.5v105.5H396.5v-47h72.3v-20h-40.5v-94.4H133.5v94.4h-32.1v20h64.2v47zm644 0h-50.6a28.5 28.5 0 0 1 42.4-6.9zM561.1 324.4V362a90.5 90.5 0 0 1 8-180.3 90.4 90.4 0 0 1 12 179.9v-29.2l56.4-56.3-14.2-14.2-42.1 42.1v-38H561v30l-37-37.1-14.3 14.2zm-407.5 96.2v-74.3h254.6v74.3zm222.8 20v47H185.7v-47zM400 780c-41.2 0-81-6.6-118.1-18.8l403.8-111A379.1 379.1 0 0 1 400 780zm307-156.4L250 749c-11.4-5-22.6-10.4-33.4-16.4L484 655l-5.5-19.3-285.3 82.8c-13.3-8.7-26-18.2-38.1-28.4l131.8-24.9-3.7-19.7-146.8 27.7a383.3 383.3 0 0 1-36-39.8l637.6-60c-9 17.5-19.4 34.2-31 50zm41-71.2L86.6 614.6a378.6 378.6 0 0 1-51-107h728.8c-4.5 15.4-10 30.3-16.4 44.7zm-69.7-64.7L682 476a51 51 0 0 1 48.6-35.4h10l.2-9.8a49.3 49.3 0 0 1 38.8-47.3 385.6 385.6 0 0 1-9.9 104z" fill="#000"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,2 @@
SPDX-FileCopyrightText: Terence Eden
SPDX-License-Identifier: LicenseRef-LOGO

View file

@ -25,156 +25,93 @@ import { GeoOperations } from "../../src/Logic/GeoOperations"
* "sqlite3": "^5.1.7", * "sqlite3": "^5.1.7",
*/ */
interface Bench { interface Bench {
benchID: number, benchID: number
latitude: number, latitude: number
longitude: number, longitude: number
address: string, address: string
inscription: string, inscription: string
description: string, description: string
present: 0 | 1, present: 0 | 1
published: 0 | 1, published: 0 | 1
/* time of creation (or possibly last edit?) */ /* time of creation (or possibly last edit?) */
added: string, added: string
userID: number userID: number
} }
interface User { interface User {
name: string, name: string
providerID: string, providerID: string
provider: string, provider: string
userID: number userID: number
} }
interface Tag { interface Tag {
tagID: number, tagID: number
tagText: string tagText: string
} }
function mediaUrl(sha: string | { "sha1": string }): string { function mediaUrl(sha: string | { sha1: string }): string {
if (sha["sha1"]) { if (sha["sha1"]) {
sha = sha["sha1"] sha = sha["sha1"]
} }
return `https://openbenches.org/image/${sha}.jpg` return `https://openbenches.org/image/${sha}.jpg`
} }
const uk: Feature<Polygon> = { const uk: Feature<Polygon> = {
"type": "Feature", type: "Feature",
"properties": {}, properties: {},
"geometry": { geometry: {
"coordinates": [ coordinates: [
[ [
[ [3.139397666817615, 53.112746745001914],
3.139397666817615, [0.12546232547020963, 61.34289409315957],
53.112746745001914 [-5.193638926198332, 60.3858935023425],
], [-12.316831332595541, 56.76308878364702],
[ [-12.586640816376246, 51.076733390490034],
0.12546232547020963, [-3.6443836396576046, 49.4256703574342],
61.34289409315957 [1.0194660085441853, 50.442813369706585],
], [3.139397666817615, 53.112746745001914],
[ ],
-5.193638926198332,
60.3858935023425
],
[
-12.316831332595541,
56.76308878364702
],
[
-12.586640816376246,
51.076733390490034
],
[
-3.6443836396576046,
49.4256703574342
],
[
1.0194660085441853,
50.442813369706585
],
[
3.139397666817615,
53.112746745001914
]
]
], ],
"type": "Polygon" type: "Polygon",
} },
} }
const us : Feature<Polygon> = { const us: Feature<Polygon> = {
"type": "Feature", type: "Feature",
"properties": {}, properties: {},
"geometry": { geometry: {
"coordinates": [ coordinates: [
[ [
[ [-171.55472370762342, 71.44263911390138],
-171.55472370762342, [-171.31347027402668, 33.24735774004321],
71.44263911390138 [-105.9804086342826, -3.5292610992716362],
], [-57.00596161415962, 15.805666337324794],
[ [-32.880618254493015, 49.584578264365916],
-171.31347027402668, [-47.35582427029317, 72.85409976292118],
33.24735774004321 [-101.60890406091582, 79.0557752859543],
], [-171.55472370762342, 71.44263911390138],
[ ],
-105.9804086342826,
-3.5292610992716362
],
[
-57.00596161415962,
15.805666337324794
],
[
-32.880618254493015,
49.584578264365916
],
[
-47.35582427029317,
72.85409976292118
],
[
-101.60890406091582,
79.0557752859543
],
[
-171.55472370762342,
71.44263911390138
]
]
], ],
"type": "Polygon" type: "Polygon",
} },
} }
const australia: Feature<Polygon> = { const australia: Feature<Polygon> = {
"type": "Feature", type: "Feature",
"properties": {}, properties: {},
"geometry": { geometry: {
"coordinates": [ coordinates: [
[ [
[ [177.6309142850211, -48.72845301037672],
177.6309142850211, [177.6309142850211, -8.050870320392335],
-48.72845301037672 [107.59695622498174, -8.050870320392335],
], [107.59695622498174, -48.72845301037672],
[ [177.6309142850211, -48.72845301037672],
177.6309142850211, ],
-8.050870320392335
],
[
107.59695622498174,
-8.050870320392335
],
[
107.59695622498174,
-48.72845301037672
],
[
177.6309142850211,
-48.72845301037672
]
]
], ],
"type": "Polygon" type: "Polygon",
} },
} }
const areas = {uk, us, australia} const areas = { uk, us, australia }
class Openbenches extends Script { class Openbenches extends Script {
private db: Database private db: Database
@ -190,18 +127,18 @@ class Openbenches extends Script {
}) })
const files = await fs.readdir(sqlDir) const files = await fs.readdir(sqlDir)
const sqlFiles = files.filter(f => f.endsWith(".sql")) const sqlFiles = files.filter((f) => f.endsWith(".sql"))
const skip = ["database.sql"] const skip = ["database.sql"]
const order = ["tags", "users", "tag_map", "media_types", "benches", "media"] const order = ["tags", "users", "tag_map", "media_types", "benches", "media"]
for (let file of order) { for (let file of order) {
console.log("Exec file", file) console.log("Exec file", file)
file = "openbenc_benches_table_" + file + ".sql" file = "openbenc_benches_table_" + file + ".sql"
let content = await fs.readFile(join(sqlDir, file), "utf-8") let content = await fs.readFile(join(sqlDir, file), "utf-8")
content = content.replaceAll("ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", "") content = content
.replaceAll("ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", "")
.replaceAll("\\'", "''") .replaceAll("\\'", "''")
await db.exec(content) await db.exec(content)
} }
@ -228,39 +165,37 @@ class Openbenches extends Script {
driver: sqlite3.Database, driver: sqlite3.Database,
}) })
return <any>db.db return <any>db.db
} }
async createBenchInfo(benchWithUser: Bench & User, tags: string[]): Promise<Feature<Point>> { async createBenchInfo(benchWithUser: Bench & User, tags: string[]): Promise<Feature<Point>> {
const id = benchWithUser.benchID const id = benchWithUser.benchID
const media = await this.all<{ const media = await this.all<{
sha1: string, sha1: string
media_type: string media_type: string
}>("SELECT * FROM media WHERE media.benchID = " + id) }>("SELECT * FROM media WHERE media.benchID = " + id)
const mediaBench = media.filter(m => m.media_type === "bench") const mediaBench = media.filter((m) => m.media_type === "bench")
const mediaInscr = media.filter(m => m.media_type === "inscription") const mediaInscr = media.filter((m) => m.media_type === "inscription")
const mediaView = media.filter(m => m.media_type === "view") const mediaView = media.filter((m) => m.media_type === "view")
const inscription = benchWithUser.inscription.replaceAll("\\r\\n", "\n") const inscription = benchWithUser.inscription.replaceAll("\\r\\n", "\n")
const properties = { const properties = {
lastModifiedTime: benchWithUser.added, lastModifiedTime: benchWithUser.added,
"openbenches:id": id, "openbenches:id": id,
inscription: inscription.slice(0,255), inscription: inscription.slice(0, 255),
amenity: "bench", amenity: "bench",
lastModifiedBy: benchWithUser.name, lastModifiedBy: benchWithUser.name,
} }
if(inscription.length >= 255){ if (inscription.length >= 255) {
properties["inscription:0"] = inscription.slice(255) properties["inscription:0"] = inscription.slice(255)
} }
let mediaMerged = Lists.dedup(mediaBench.concat(mediaInscr).map(m => mediaUrl(m))) let mediaMerged = Lists.dedup(mediaBench.concat(mediaInscr).map((m) => mediaUrl(m)))
for (let i = 0; i < mediaMerged.length; i++) { for (let i = 0; i < mediaMerged.length; i++) {
const m = mediaMerged[i] const m = mediaMerged[i]
if (i === 0) { if (i === 0) {
properties["image"] = m properties["image"] = m
} else { } else {
properties["image:" + (i - 1)] = m properties["image:" + (i - 1)] = m
} }
} }
@ -270,18 +205,17 @@ class Openbenches extends Script {
properties["image:view"] = mediaUrl(m) properties["image:view"] = mediaUrl(m)
} else { } else {
properties["image:view:" + (i - 1)] = mediaUrl(m) properties["image:view:" + (i - 1)] = mediaUrl(m)
} }
} }
const tagsToProperties = { const tagsToProperties = {
"wooden": "material=wood", wooden: "material=wood",
"metal": "material=metal", metal: "material=metal",
"indoors": "indoor=yes", indoors: "indoor=yes",
"stone": "material=stone", stone: "material=stone",
"poem": "artwork=poem", poem: "artwork=poem",
"statue": "artwork=statue", statue: "artwork=statue",
"composite": "material=plastic", composite: "material=plastic",
/*"cat":"subject=cat", /*"cat":"subject=cat",
"dog":"subject=dog" Not always a pet, sometimes also a 'dogwalker', someone mentioning their cat, ... */ "dog":"subject=dog" Not always a pet, sometimes also a 'dogwalker', someone mentioning their cat, ... */
// EMOJI: very broad category, basically that a little image is part of the 'inscription'. Should be handled by adding the emoji directly // EMOJI: very broad category, basically that a little image is part of the 'inscription'. Should be handled by adding the emoji directly
@ -291,7 +225,7 @@ class Openbenches extends Script {
// FUnny: talk about subjective... // FUnny: talk about subjective...
} }
for (const tag of (tags ?? [])) { for (const tag of tags ?? []) {
const match = tagsToProperties[tag] const match = tagsToProperties[tag]
if (!match) { if (!match) {
continue continue
@ -314,7 +248,10 @@ class Openbenches extends Script {
async getAlreadyImported(): Promise<FeatureCollection> { async getAlreadyImported(): Promise<FeatureCollection> {
const alreadyImportedPath = "openbenches_linked_in_osm.geojson" const alreadyImportedPath = "openbenches_linked_in_osm.geojson"
if (!existsSync(alreadyImportedPath)) { if (!existsSync(alreadyImportedPath)) {
const overpass = new Overpass(Constants.defaultOverpassUrls[0], TagUtils.Tag("openbenches:id~*")) const overpass = new Overpass(
Constants.defaultOverpassUrls[0],
TagUtils.Tag("openbenches:id~*")
)
const dataAndDate = await overpass.queryGeoJson(BBox.global) const dataAndDate = await overpass.queryGeoJson(BBox.global)
const data = dataAndDate[0] const data = dataAndDate[0]
writeFileSync(alreadyImportedPath, JSON.stringify(data), "utf-8") writeFileSync(alreadyImportedPath, JSON.stringify(data), "utf-8")
@ -345,7 +282,11 @@ class Openbenches extends Script {
} }
if (key.startsWith("image")) { if (key.startsWith("image")) {
const imgValue = ob.properties[key] const imgValue = ob.properties[key]
if (Object.values(bench.properties).some(v => v === imgValue || (v + ".jpg") === imgValue)) { if (
Object.values(bench.properties).some(
(v) => v === imgValue || v + ".jpg" === imgValue
)
) {
continue continue
} }
let ikey = "image" let ikey = "image"
@ -354,37 +295,44 @@ class Openbenches extends Script {
i++ i++
ikey = "image:" + i ikey = "image:" + i
} }
const li = new ChangeTagAction(bench.properties.id, new OsmTag(ikey, imgValue), bench.properties, { const li = new ChangeTagAction(
theme: "openbenches", bench.properties.id,
changeType: "link-image", new OsmTag(ikey, imgValue),
}) bench.properties,
{
theme: "openbenches",
changeType: "link-image",
}
)
changes.push(li) changes.push(li)
bench.properties[ikey] = imgValue bench.properties[ikey] = imgValue
console.log(` + ${ikey}=${imgValue}`) console.log(` + ${ikey}=${imgValue}`)
} else if (!bench.properties[key]) { } else if (!bench.properties[key]) {
const v = ob.properties[key] const v = ob.properties[key]
if(v.length >= 255){ if (v.length >= 255) {
console.log("Text too long:", v.replaceAll("\n"," ")) console.log("Text too long:", v.replaceAll("\n", " "))
continue continue
} }
changes.push(new ChangeTagAction( changes.push(
bench.properties.id, new ChangeTagAction(
new OsmTag(key, v), bench.properties.id,
bench.properties, new OsmTag(key, v),
{ bench.properties,
theme: "openbenches", {
changeType: "answer", theme: "openbenches",
}, changeType: "answer",
)) }
)
)
console.log(` - ${key}=${ob.properties[key].replaceAll("\n", " ")}`) console.log(` - ${key}=${ob.properties[key].replaceAll("\n", " ")}`)
} }
} }
} }
if(changes.length === 0){ if (changes.length === 0) {
return return
} }
const xml = await Changes.createChangesetXMLForJosm(changes) const xml = await Changes.createChangesetXMLForJosm(changes)
writeFileSync(`attributes_import${area}.osc`,xml, "utf-8") writeFileSync(`attributes_import${area}.osc`, xml, "utf-8")
} }
async main(args: string[]): Promise<void> { async main(args: string[]): Promise<void> {
@ -394,12 +342,13 @@ class Openbenches extends Script {
const osmData = await this.getAlreadyImported() const osmData = await this.getAlreadyImported()
// rmSync(dbFile) // rmSync(dbFile)
if(!existsSync(dbFile)){ if (!existsSync(dbFile)) {
console.log("No database file found at "+dbFile+", recreating the database") console.log("No database file found at " + dbFile + ", recreating the database")
await this.buildDatabase("/home/pietervdvn/git/openbenches.org/database", dbFile) await this.buildDatabase("/home/pietervdvn/git/openbenches.org/database", dbFile)
} }
const alreadyLinked: Set<number> = new Set(osmData.features.map(f => Number(f.properties["openbenches:id"]))) const alreadyLinked: Set<number> = new Set(
osmData.features.map((f) => Number(f.properties["openbenches:id"]))
)
this.db = await this.loadDb(dbFile) this.db = await this.loadDb(dbFile)
@ -409,7 +358,9 @@ class Openbenches extends Script {
tags.set(tag.tagID, tag.tagText) tags.set(tag.tagID, tag.tagText)
} }
const tagsOnBenches = new Map<number, string[]>() const tagsOnBenches = new Map<number, string[]>()
const tagOnBench = await this.all<{ benchID: number, tagID: number }>("SELECT * from tag_map") const tagOnBench = await this.all<{ benchID: number; tagID: number }>(
"SELECT * from tag_map"
)
for (const tg of tagOnBench) { for (const tg of tagOnBench) {
const bench = tg.benchID const bench = tg.benchID
if (!tagsOnBenches.has(bench)) { if (!tagsOnBenches.has(bench)) {
@ -418,11 +369,13 @@ class Openbenches extends Script {
tagsOnBenches.get(bench).push(tags.get(tg.tagID)) tagsOnBenches.get(bench).push(tags.get(tg.tagID))
} }
const openbenches = await this.all<Bench & User>("SELECT * FROM benches INNER JOIN users ON benches.userID = users.userID") const openbenches = await this.all<Bench & User>(
"SELECT * FROM benches INNER JOIN users ON benches.userID = users.userID"
)
const features: Feature<Point>[] = [] const features: Feature<Point>[] = []
let skipped = 0 let skipped = 0
for (let i = 0; i < openbenches.length; i++) { for (let i = 0; i < openbenches.length; i++) {
if(alreadyLinked.has(i)){ if (alreadyLinked.has(i)) {
skipped++ skipped++
continue continue
} }
@ -432,33 +385,50 @@ class Openbenches extends Script {
} }
const tags = tagsOnBenches.get(benchWithUser.benchID) const tags = tagsOnBenches.get(benchWithUser.benchID)
if (i % 100 === 0) { if (i % 100 === 0) {
ScriptUtils.erasableLog(`Processing bench ${i}/${openbenches.length} (${Math.round(100 * i / openbenches.length)}%) `) ScriptUtils.erasableLog(
`Processing bench ${i}/${openbenches.length} (${Math.round(
(100 * i) / openbenches.length
)}%) `
)
} }
features.push(await this.createBenchInfo(benchWithUser, tags)) features.push(await this.createBenchInfo(benchWithUser, tags))
if (createTest && features.length > 1000) { if (createTest && features.length > 1000) {
break break
} }
} }
/* /*
writeFileSync(`openbenches_export_josm_${createTest ? "_test" : ""}.geojson`, JSON.stringify({ writeFileSync(`openbenches_export_josm_${createTest ? "_test" : ""}.geojson`, JSON.stringify({
type: "FeatureCollection", features, type: "FeatureCollection", features,
}, null, " "), "utf-8")*/ }, null, " "), "utf-8")*/
const maproulette = features const maproulette = features.map((f) => {
.map(f => { const fProps = {
const properties = {tags: JSON.stringify(f.properties)} ...f.properties
properties["id"] = "openbenches/"+f.properties["openbenches:id"] }
return {...f, properties}
delete fProps["lastModifiedBy"]
delete fProps["lastModifiedTime"]
const properties = { ...f.properties, tags: JSON.stringify(fProps) }
delete properties["amenity"] // Makes sure mapcomplete doesn't think this is a bench...
properties["id"] = "openbenches/" + f.properties["openbenches:id"]
return { ...f, properties }
}) })
console.log("Skipped",skipped,"benches as already linked/imported") console.log("Skipped", skipped, "benches as already linked/imported")
writeFileSync(`openbenches_export_maproulette${createTest ? "_test" : ""}.geojson`, JSON.stringify({ writeFileSync(
type: "FeatureCollection", features: maproulette, `openbenches_export_maproulette${createTest ? "_test" : ""}.geojson`,
}, null, " "), "utf-8") JSON.stringify(
{
type: "FeatureCollection",
features: maproulette,
},
null,
" "
),
"utf-8"
)
await this.conflate(osmData.features, { type: "FeatureCollection", features }, "_all") await this.conflate(osmData.features, { type: "FeatureCollection", features }, "_all")
} }
} }