Fix: maproulette import flow
This commit is contained in:
parent
f80054558f
commit
5f7cc351c9
18 changed files with 331 additions and 114 deletions
|
@ -254,6 +254,7 @@ class ClosestNObjectFunc implements ExtraFunction {
|
|||
const maxDistance = options?.maxDistance ?? 500
|
||||
const uniqueTag: string | undefined = options?.uniqueTag
|
||||
let allFeatures: Feature[][]
|
||||
console.log("Calculating closest", options?.maxFeatures, "features around", feature, "in layer", features)
|
||||
if (typeof features === "string") {
|
||||
const name = features
|
||||
const bbox = GeoOperations.bbox(
|
||||
|
@ -414,7 +415,7 @@ class GetParsed implements ExtraFunction {
|
|||
if (value === undefined) {
|
||||
return undefined
|
||||
}
|
||||
if(typeof value !== "string"){
|
||||
if (typeof value !== "string") {
|
||||
return value
|
||||
}
|
||||
try {
|
||||
|
|
|
@ -105,6 +105,10 @@ export default class GeoJsonSource implements FeatureSource {
|
|||
let i = 0
|
||||
let skipped = 0
|
||||
for (const feature of json.features) {
|
||||
if(feature.geometry.type === "Point"){
|
||||
// See https://github.com/maproulette/maproulette-backend/issues/242
|
||||
feature.geometry.coordinates = feature.geometry.coordinates.map(Number)
|
||||
}
|
||||
const props = feature.properties
|
||||
for (const key in props) {
|
||||
if (props[key] === null) {
|
||||
|
|
|
@ -72,4 +72,23 @@ export default class Maproulette {
|
|||
throw `Failed to close task: ${response.status}`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a status text into the corresponding number
|
||||
*
|
||||
* Maproulette.codeToIndex("Created") // => 0
|
||||
* Maproulette.codeToIndex("qdsf") // => undefined
|
||||
*
|
||||
*/
|
||||
public static codeToIndex(code: string) : number | undefined{
|
||||
if(code === "Created"){
|
||||
return Maproulette.STATUS_OPEN
|
||||
}
|
||||
for (let i = 0; i < 9; i++) {
|
||||
if(Maproulette.STATUS_MEANING[""+i] === code){
|
||||
return i
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import {GeoIndexedStoreForLayer} from "./FeatureSource/Actors/GeoIndexedStore"
|
|||
import {IndexedFeatureSource} from "./FeatureSource/FeatureSource"
|
||||
import OsmObjectDownloader from "./Osm/OsmObjectDownloader"
|
||||
import {Utils} from "../Utils";
|
||||
import {GeoJSONFeature} from "maplibre-gl";
|
||||
import {UIEventSource} from "./UIEventSource";
|
||||
|
||||
/**
|
||||
|
@ -206,13 +205,13 @@ export default class MetaTagging {
|
|||
private static createFunctionForFeature([key, code, isStrict]: [string, string, boolean],
|
||||
helperFunctions: Record<ExtraFuncType, (feature: Feature) => Function>,
|
||||
layerId: string = "unkown layer"
|
||||
): ((feature: GeoJSONFeature, propertiesStore?: UIEventSource<any>) => void) | undefined {
|
||||
): ((feature: Feature, propertiesStore?: UIEventSource<any>) => void) | undefined {
|
||||
if (code === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
||||
const calculateAndAssign: ((feat: GeoJSONFeature, store?: UIEventSource<any>) => string | any) = (feat, store) => {
|
||||
const calculateAndAssign: ((feat: Feature, store?: UIEventSource<any>) => string | any) = (feat, store) => {
|
||||
try {
|
||||
let result = new Function("feat", "{" + ExtraFunctions.types.join(", ") + "}", "return " + code + ";")(feat, helperFunctions)
|
||||
if (result === "") {
|
||||
|
@ -259,7 +258,7 @@ export default class MetaTagging {
|
|||
if (isStrict) {
|
||||
return calculateAndAssign
|
||||
}
|
||||
return (feature: any, store?: UIEventSource<any>) => {
|
||||
return (feature: Feature, store?: UIEventSource<any>) => {
|
||||
delete feature.properties[key]
|
||||
Utils.AddLazyProperty(feature.properties, key, () => calculateAndAssign(feature, store))
|
||||
}
|
||||
|
|
|
@ -132,6 +132,10 @@ class CountryTagger extends SimpleMetaTagger {
|
|||
CountryTagger.coder
|
||||
.GetCountryCodeAsync(lon, lat)
|
||||
.then((countries) => {
|
||||
if(!countries){
|
||||
console.warn("Country coder returned ", countries)
|
||||
return
|
||||
}
|
||||
const oldCountry = feature.properties["_country"]
|
||||
const newCountry = countries[0].trim().toLowerCase()
|
||||
if (oldCountry !== newCountry) {
|
||||
|
|
|
@ -538,7 +538,6 @@ export default class TagRenderingConfig {
|
|||
}
|
||||
|
||||
if (
|
||||
this.id === "questions" ||
|
||||
this.freeform?.key === undefined ||
|
||||
tags[this.freeform.key] !== undefined
|
||||
) {
|
||||
|
|
|
@ -116,11 +116,12 @@
|
|||
currentFlowStep = "imported"
|
||||
dispatch("confirm")
|
||||
}}>
|
||||
<span slot="image">
|
||||
|
||||
{#if importFlow.args.icon}
|
||||
<img src={importFlow.args.icon}>
|
||||
{/if}
|
||||
<span slot="image" class="w-8 h-8 pr-4">
|
||||
{#if importFlow.args.icon}
|
||||
<img src={importFlow.args.icon}>
|
||||
{:else}
|
||||
<ToSvelte construct={Svg.confirm_svg().SetClass("w-8 h-8 pr-4")}/>
|
||||
{/if}
|
||||
</span>
|
||||
<slot name="confirm-text">
|
||||
{importFlow.args.text}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {SpecialVisualizationState} from "../../SpecialVisualization";
|
||||
import {Utils} from "../../../Utils";
|
||||
import {Store, UIEventSource} from "../../../Logic/UIEventSource";
|
||||
import {ImmutableStore, Store, UIEventSource} from "../../../Logic/UIEventSource";
|
||||
import {Tag} from "../../../Logic/Tags/Tag";
|
||||
import TagApplyButton from "../TagApplyButton";
|
||||
import {PointImportFlowArguments} from "./PointImportFlowState";
|
||||
|
@ -11,6 +11,7 @@ import FilteredLayer from "../../../Models/FilteredLayer";
|
|||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig";
|
||||
import {LayerConfigJson} from "../../../Models/ThemeConfig/Json/LayerConfigJson";
|
||||
import conflation_json from "../../../assets/layers/conflation/conflation.json";
|
||||
import {And} from "../../../Logic/Tags/And";
|
||||
|
||||
export interface ImportFlowArguments {
|
||||
readonly text: string
|
||||
|
@ -85,6 +86,15 @@ ${Utils.special_visualizations_importRequirementDocs}
|
|||
"] of this object, namely ",
|
||||
items
|
||||
)
|
||||
|
||||
if(items.startsWith("{")){
|
||||
// This is probably a JSON
|
||||
const properties: Record<string, string> = JSON.parse(items)
|
||||
const keys = Object.keys(properties)
|
||||
const tags = keys.map(k => new Tag(k, properties[k]))
|
||||
return new ImmutableStore((tags))
|
||||
}
|
||||
|
||||
newTags = TagApplyButton.generateTagsToApply(items, originalFeatureTags)
|
||||
} else {
|
||||
newTags = TagApplyButton.generateTagsToApply(tags, originalFeatureTags)
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
import { AutoAction } from "./AutoApplyButton"
|
||||
import {AutoAction} from "./AutoApplyButton"
|
||||
import Translations from "../i18n/Translations"
|
||||
import { VariableUiElement } from "../Base/VariableUIElement"
|
||||
import {VariableUiElement} from "../Base/VariableUIElement"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import { FixedUiElement } from "../Base/FixedUiElement"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { SubtleButton } from "../Base/SubtleButton"
|
||||
import {FixedUiElement} from "../Base/FixedUiElement"
|
||||
import {Store, UIEventSource} from "../../Logic/UIEventSource"
|
||||
import {SubtleButton} from "../Base/SubtleButton"
|
||||
import Combine from "../Base/Combine"
|
||||
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
|
||||
import { And } from "../../Logic/Tags/And"
|
||||
import {And} from "../../Logic/Tags/And"
|
||||
import Toggle from "../Input/Toggle"
|
||||
import { Utils } from "../../Utils"
|
||||
import { Tag } from "../../Logic/Tags/Tag"
|
||||
import {Utils} from "../../Utils"
|
||||
import {Tag} from "../../Logic/Tags/Tag"
|
||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import { Changes } from "../../Logic/Osm/Changes"
|
||||
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import {Changes} from "../../Logic/Osm/Changes"
|
||||
import {SpecialVisualization, SpecialVisualizationState} from "../SpecialVisualization"
|
||||
import {IndexedFeatureSource} from "../../Logic/FeatureSource/FeatureSource";
|
||||
import {Feature} from "geojson";
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
||||
import Maproulette from "../../Logic/Maproulette";
|
||||
|
||||
export default class TagApplyButton implements AutoAction, SpecialVisualization {
|
||||
public readonly funcName = "tag_apply"
|
||||
|
@ -27,7 +28,7 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
|
|||
public readonly args = [
|
||||
{
|
||||
name: "tags_to_apply",
|
||||
doc: "A specification of the tags to apply",
|
||||
doc: "A specification of the tags to apply. This is either hardcoded in the layer or the `$name` of a property containing the tags to apply. If redirected and the value of the linked property starts with `{`, the other property will be interpreted as a json object",
|
||||
},
|
||||
{
|
||||
name: "message",
|
||||
|
@ -42,10 +43,62 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
|
|||
defaultValue: undefined,
|
||||
doc: "If specified, applies the the tags onto _another_ object. The id will be read from properties[id_of_object_to_apply_this_one] of the selected object. The tags are still calculated based on the tags of the _selected_ element",
|
||||
},
|
||||
{
|
||||
name:"maproulette_task_id",
|
||||
defaultValue: undefined,
|
||||
doc: "If specified, this maproulette-challenge will be closed when the tags are applied"
|
||||
}
|
||||
]
|
||||
public readonly example =
|
||||
"`{tag_apply(survey_date=$_now:date, Surveyed today!)}`, `{tag_apply(addr:street=$addr:street, Apply the address, apply_icon.svg, _closest_osm_id)"
|
||||
|
||||
public static generateTagsToApply(
|
||||
spec: string,
|
||||
tagSource: Store<Record<string, string>>
|
||||
): Store<Tag[]> {
|
||||
// Check whether we need to look up a single value
|
||||
|
||||
|
||||
if (!spec.includes(";") && !spec.includes("=") && spec.startsWith("$")) {
|
||||
// We seem to be dealing with a single value, fetch it
|
||||
spec = tagSource.data[spec.replace("$", "")]
|
||||
|
||||
}
|
||||
|
||||
let tgsSpec: [string, string][]
|
||||
|
||||
if (spec.startsWith("{")) {
|
||||
const properties = JSON.parse(spec)
|
||||
tgsSpec = []
|
||||
for (const key of Object.keys(properties)) {
|
||||
tgsSpec.push([key, properties[key]])
|
||||
}
|
||||
} else {
|
||||
tgsSpec = TagApplyButton.parseTagSpec(spec)
|
||||
}
|
||||
|
||||
return tagSource.map((tags) => {
|
||||
const newTags: Tag[] = []
|
||||
for (const [key, value] of tgsSpec) {
|
||||
if (value.indexOf("$") >= 0) {
|
||||
let parts = value.split("$")
|
||||
// THe first of the split won't start with a '$', so no substitution needed
|
||||
let actualValue = parts[0]
|
||||
parts.shift()
|
||||
|
||||
for (const part of parts) {
|
||||
const [_, varName, leftOver] = part.match(/([a-zA-Z0-9_:]*)(.*)/)
|
||||
actualValue += (tags[varName] ?? "") + leftOver
|
||||
}
|
||||
newTags.push(new Tag(key, actualValue))
|
||||
} else {
|
||||
newTags.push(new Tag(key, value))
|
||||
}
|
||||
}
|
||||
return newTags
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a tag specification
|
||||
*
|
||||
|
@ -79,41 +132,6 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
|
|||
return tgsSpec
|
||||
}
|
||||
|
||||
public static generateTagsToApply(
|
||||
spec: string,
|
||||
tagSource: Store<Record<string, string>>
|
||||
): Store<Tag[]> {
|
||||
// Check whether we need to look up a single value
|
||||
|
||||
if (!spec.includes(";") && !spec.includes("=") && spec.includes("$")) {
|
||||
// We seem to be dealing with a single value, fetch it
|
||||
spec = tagSource.data[spec.replace("$", "")]
|
||||
}
|
||||
|
||||
const tgsSpec = TagApplyButton.parseTagSpec(spec)
|
||||
|
||||
return tagSource.map((tags) => {
|
||||
const newTags: Tag[] = []
|
||||
for (const [key, value] of tgsSpec) {
|
||||
if (value.indexOf("$") >= 0) {
|
||||
let parts = value.split("$")
|
||||
// THe first of the split won't start with a '$', so no substitution needed
|
||||
let actualValue = parts[0]
|
||||
parts.shift()
|
||||
|
||||
for (const part of parts) {
|
||||
const [_, varName, leftOver] = part.match(/([a-zA-Z0-9_:]*)(.*)/)
|
||||
actualValue += (tags[varName] ?? "") + leftOver
|
||||
}
|
||||
newTags.push(new Tag(key, actualValue))
|
||||
} else {
|
||||
newTags.push(new Tag(key, value))
|
||||
}
|
||||
}
|
||||
return newTags
|
||||
})
|
||||
}
|
||||
|
||||
public async applyActionOn(
|
||||
feature: Feature,
|
||||
state: {
|
||||
|
@ -123,7 +141,6 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
|
|||
},
|
||||
tags: UIEventSource<any>,
|
||||
args: string[],
|
||||
|
||||
): Promise<void> {
|
||||
const tagsToApply = TagApplyButton.generateTagsToApply(args[0], tags)
|
||||
const targetIdKey = args[3]
|
||||
|
@ -139,6 +156,13 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
|
|||
}
|
||||
)
|
||||
await state.changes.applyAction(changeAction)
|
||||
const maproulette_id_key = args[4]
|
||||
if(maproulette_id_key){
|
||||
const maproulette_id = Number(tags.data[maproulette_id_key])
|
||||
await Maproulette.singleton.closeTask(maproulette_id, Maproulette.STATUS_FIXED, {
|
||||
comment: "Tags are copied onto "+targetId+" with MapComplete"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public constr(
|
||||
|
@ -163,7 +187,7 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
|
|||
let el: BaseUIElement = new FixedUiElement(tagsStr)
|
||||
if (targetIdKey !== undefined) {
|
||||
const targetId = tags.data[targetIdKey] ?? tags.data.id
|
||||
el = t.appliedOnAnotherObject.Subs({ tags: tagsStr, id: targetId })
|
||||
el = t.appliedOnAnotherObject.Subs({tags: tagsStr, id: targetId})
|
||||
}
|
||||
return el
|
||||
})
|
||||
|
|
|
@ -11,27 +11,27 @@
|
|||
|
||||
export let tags: UIEventSource<Record<string, string> | undefined>;
|
||||
let _tags: Record<string, string>;
|
||||
onDestroy(tags.addCallbackAndRun(tags => {
|
||||
_tags = tags;
|
||||
}));
|
||||
let trs: { then: Translation; icon?: string; iconClass?: string }[];
|
||||
|
||||
export let state: SpecialVisualizationState;
|
||||
export let selectedElement: Feature;
|
||||
export let layer: LayerConfig;
|
||||
export let config: TagRenderingConfig;
|
||||
export let extraClasses: string= ""
|
||||
|
||||
if (config === undefined) {
|
||||
throw "Config is undefined in tagRenderingAnswer";
|
||||
}
|
||||
export let layer: LayerConfig;
|
||||
let trs: { then: Translation; icon?: string; iconClass?: string }[];
|
||||
$:{
|
||||
onDestroy(tags.addCallbackAndRun(tags => {
|
||||
_tags = tags;
|
||||
trs = Utils.NoNull(config?.GetRenderValues(_tags));
|
||||
}
|
||||
export let extraClasses: string= ""
|
||||
}));
|
||||
let classes = ""
|
||||
$:classes = config?.classes?.join(" ") ?? "";
|
||||
</script>
|
||||
|
||||
{#if config !== undefined && (config?.condition === undefined || config.condition.matchesProperties(_tags))}
|
||||
<div class={"link-underline flex flex-col w-full overflow-hidden "+classes+" "+extraClasses}>
|
||||
<div class={"link-underline flex flex-col w-full "+classes+" "+extraClasses}>
|
||||
{#if trs.length === 1}
|
||||
<TagRenderingMapping mapping={trs[0]} {tags} {state} {selectedElement} {layer}></TagRenderingMapping>
|
||||
{/if}
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
<XCircleIcon slot="upper-right" class="w-8 h-8 cursor-pointer" on:click={() => {editMode = false}}/>
|
||||
</TagRenderingQuestion>
|
||||
{:else}
|
||||
<div class="flex justify-between low-interaction items-center rounded px-2">
|
||||
<div class="flex justify-between low-interaction items-center rounded px-2 overflow-hidden">
|
||||
<TagRenderingAnswer {config} {tags} {selectedElement} {state} {layer}/>
|
||||
<button on:click={() => {editMode = true}}
|
||||
class="shrink-0 w-8 h-8 rounded-full p-1 secondary self-start">
|
||||
|
@ -87,6 +87,8 @@
|
|||
</div>
|
||||
{/if}
|
||||
{:else }
|
||||
<div class="p-2 overflow-hidden">
|
||||
<TagRenderingAnswer {config} {tags} {selectedElement} {state} {layer}/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
@ -959,24 +959,6 @@ export default class SpecialVisualizations {
|
|||
defaultValue: "id",
|
||||
},
|
||||
],
|
||||
example:
|
||||
" The following example sets the status to '2' (false positive)\n" +
|
||||
"\n" +
|
||||
"```json\n" +
|
||||
"{\n" +
|
||||
' "id": "mark_duplicate",\n' +
|
||||
' "render": {\n' +
|
||||
' "special": {\n' +
|
||||
' "type": "maproulette_set_status",\n' +
|
||||
' "message": {\n' +
|
||||
' "en": "Mark as not found or false positive"\n' +
|
||||
" },\n" +
|
||||
' "status": "2",\n' +
|
||||
' "image": "close"\n' +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}\n" +
|
||||
"```",
|
||||
constr: (state, tags, args) => {
|
||||
const isUploading = new UIEventSource(false)
|
||||
const t = Translations.t.notes
|
||||
|
@ -1080,6 +1062,24 @@ export default class SpecialVisualizations {
|
|||
{
|
||||
funcName: "maproulette_set_status",
|
||||
docs: "Change the status of the given MapRoulette task",
|
||||
example:
|
||||
" The following example sets the status to '2' (false positive)\n" +
|
||||
"\n" +
|
||||
"```json\n" +
|
||||
"{\n" +
|
||||
' "id": "mark_duplicate",\n' +
|
||||
' "render": {\n' +
|
||||
' "special": {\n' +
|
||||
' "type": "maproulette_set_status",\n' +
|
||||
' "message": {\n' +
|
||||
' "en": "Mark as not found or false positive"\n' +
|
||||
" },\n" +
|
||||
' "status": "2",\n' +
|
||||
' "image": "close"\n' +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}\n" +
|
||||
"```",
|
||||
args: [
|
||||
{
|
||||
name: "message",
|
||||
|
@ -1110,11 +1110,14 @@ export default class SpecialVisualizations {
|
|||
if (image === "") {
|
||||
image = "confirm"
|
||||
}
|
||||
if(maproulette_id_key === "" || maproulette_id_key === undefined){
|
||||
maproulette_id_key = "mr_taskId"
|
||||
}
|
||||
if (Svg.All[image] !== undefined || Svg.All[image + ".svg"] !== undefined) {
|
||||
if (image.endsWith(".svg")) {
|
||||
image = image.substring(0, image.length - 4)
|
||||
}
|
||||
image = Svg[image + "_ui"]()
|
||||
image = Svg[image + "_svg"]()
|
||||
}
|
||||
const failed = new UIEventSource(false)
|
||||
|
||||
|
@ -1122,7 +1125,7 @@ export default class SpecialVisualizations {
|
|||
Translations.t.general.loading,
|
||||
async () => {
|
||||
const maproulette_id =
|
||||
tagsSource.data[maproulette_id_key] ?? tagsSource.data.id
|
||||
tagsSource.data[maproulette_id_key] ?? tagsSource.data.mr_taskId ?? tagsSource.data.id
|
||||
try {
|
||||
await Maproulette.singleton.closeTask(
|
||||
Number(maproulette_id),
|
||||
|
@ -1150,13 +1153,19 @@ export default class SpecialVisualizations {
|
|||
return new VariableUiElement(
|
||||
tagsSource
|
||||
.map(
|
||||
(tgs) =>
|
||||
tgs["status"] ??
|
||||
Maproulette.STATUS_MEANING[tgs["mr_taskStatus"]]
|
||||
(tgs) => {
|
||||
if(tgs["status"]){
|
||||
return tgs["status"]
|
||||
}
|
||||
const code = tgs["mr_taskStatus"]
|
||||
console.log("Code is", code, Maproulette.codeToIndex(code))
|
||||
return Maproulette.codeToIndex(code)
|
||||
}
|
||||
)
|
||||
.map(Number)
|
||||
.map(
|
||||
(status) => {
|
||||
console.log("Close MR button: status is", status)
|
||||
if (failed.data) {
|
||||
return new FixedUiElement(
|
||||
"ERROR - could not close the MapRoulette task"
|
||||
|
|
|
@ -60,7 +60,6 @@
|
|||
"tagRenderings": [
|
||||
{
|
||||
"id": "status",
|
||||
"render": "Current status: {status}",
|
||||
"mappings": [
|
||||
{
|
||||
"if": "status=0",
|
||||
|
@ -130,6 +129,7 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"labels": ["controls"],
|
||||
"id": "mark_fixed",
|
||||
"render": {
|
||||
"special": {
|
||||
|
@ -144,6 +144,7 @@
|
|||
},
|
||||
{
|
||||
"id": "mark_duplicate",
|
||||
"labels": ["controls"],
|
||||
"render": {
|
||||
"special": {
|
||||
"type": "maproulette_set_status",
|
||||
|
@ -159,6 +160,7 @@
|
|||
},
|
||||
{
|
||||
"id": "mark_too_hard",
|
||||
"labels": ["controls"],
|
||||
"render": {
|
||||
"special": {
|
||||
"type": "maproulette_set_status",
|
||||
|
@ -306,4 +308,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,6 @@
|
|||
},
|
||||
{
|
||||
"id": "status",
|
||||
"render": "Current status: {status}",
|
||||
"mappings": [
|
||||
{
|
||||
"if": "mr_taskStatus=Created",
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
"override": {
|
||||
"id": "banks_with_atm",
|
||||
"name": null,
|
||||
"minzoom": 14,
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and+": [
|
||||
|
@ -54,6 +55,68 @@
|
|||
"sameAs": "bank"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"builtin": "maproulette_challenge",
|
||||
"override": {
|
||||
"minzoom": 5,
|
||||
"source": {
|
||||
"geoJson": "https://maproulette.org/api/v2/challenge/view/39519"
|
||||
},
|
||||
"isShown": "mr_taskStatus=Created",
|
||||
"calculatedTags": [
|
||||
"_closest_osm_poi=closest(feat)('atm')?.properties?.id",
|
||||
"_closest_osm_poi_distance=Math.round(distanceTo(feat)(feat.properties._closest_osm_poi))",
|
||||
"_has_closeby_feature=Number(feat.properties._closest_osm_poi_distance) < 50 ? 'yes' : 'no'"
|
||||
],
|
||||
"=tagRenderings": [
|
||||
{
|
||||
"id": "import-button",
|
||||
"condition": "_has_closeby_feature=no",
|
||||
"render": {
|
||||
"special": {
|
||||
"type": "import_button",
|
||||
"targetLayer": "atm",
|
||||
"tags": "tags",
|
||||
"maproulette_id": "mr_taskId",
|
||||
"text": {
|
||||
"en": "Import this ATM"
|
||||
},
|
||||
"icon": "./assets/svg/addSmall.svg"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "closeness-indicator",
|
||||
"condition": "_has_closeby_feature=yes",
|
||||
"render": {
|
||||
"en": "OpenStreetMap knows about <a href='#{_closest_osm_poi}'>an ATM which is {_closest_osm_poi_distance} meter away.</a> "
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "tag-apply-button",
|
||||
"condition": "_has_closeby_feature=yes",
|
||||
"render": {
|
||||
"special": {
|
||||
"type": "tag_apply",
|
||||
"tags_to_apply": "$tags",
|
||||
"id_of_object_to_apply_this_one": "_closest_osm_poi",
|
||||
"message": {
|
||||
"en": "Add all the suggested tags to the closest ATM"
|
||||
},
|
||||
"image": "./assets/svg/addSmall.svg",
|
||||
"maproulette_task_id": "mr_taskId"
|
||||
}
|
||||
}
|
||||
},
|
||||
"maproulette.controls",
|
||||
{
|
||||
"id": "minimap_with_atm",
|
||||
"render": "{minimap(18, id, _closest_osm_poi)}"
|
||||
},
|
||||
"all_tags"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -356,7 +356,12 @@ class LayerOverviewUtils extends Script {
|
|||
const context = "While building builtin layer " + sharedLayerPath
|
||||
const fixed = prepLayer.convertStrict(parsed, context)
|
||||
|
||||
if (typeof fixed.source !== "string" && fixed.source["osmTags"]["and"] === undefined) {
|
||||
if(!fixed.source){
|
||||
console.error(sharedLayerPath,"has no source configured:",fixed)
|
||||
throw sharedLayerPath+" layer has no source configured"
|
||||
}
|
||||
|
||||
if (typeof fixed.source !== "string" && fixed.source["osmTags"] && fixed.source["osmTags"]["and"] === undefined) {
|
||||
fixed.source["osmTags"] = { and: [fixed.source["osmTags"]] }
|
||||
}
|
||||
|
||||
|
|
92
scripts/importscripts/cash.ts
Normal file
92
scripts/importscripts/cash.ts
Normal file
|
@ -0,0 +1,92 @@
|
|||
import fs from "fs"
|
||||
import {OH} from "../../UI/OpeningHours/OpeningHours";
|
||||
|
||||
const cashpunten = JSON.parse(fs.readFileSync("/home/pietervdvn/Downloads/cash_punten.json", "utf8")).data
|
||||
|
||||
const features: any[] = []
|
||||
const weekdays = [
|
||||
"MO",
|
||||
"TU",
|
||||
"WE",
|
||||
"TH",
|
||||
"FR",
|
||||
"SA",
|
||||
"SU"
|
||||
]
|
||||
for (const atm of cashpunten) {
|
||||
const properties = {
|
||||
"amenity": "atm",
|
||||
"addr:street": atm.adr_street,
|
||||
"addr:housenumber": atm.adr_street_number,
|
||||
"phone": <string>atm.phone_number,
|
||||
"operator": "Batopin",
|
||||
network: "CASH",
|
||||
fee: "no",
|
||||
"speech_output": "yes",
|
||||
"brand": "CASH",
|
||||
website: "https://batopin.be",
|
||||
"source": "https://batopin.be",
|
||||
"brand:wikidata": "Q112875867",
|
||||
"operator:wikidata": "Q97142699",
|
||||
"currency:EUR": "yes"
|
||||
}
|
||||
features.push({
|
||||
geometry: {type: "Point", coordinates: [atm.adr_longitude, atm.adr_latitude]},
|
||||
properties: {
|
||||
tags: properties
|
||||
}
|
||||
})
|
||||
|
||||
switch (atm.accessibility) {
|
||||
case "Green":
|
||||
properties["wheelchair"] = "yes";
|
||||
break;
|
||||
case "Orange":
|
||||
properties["wheelchair"] = "limited";
|
||||
break;
|
||||
case "Red":
|
||||
properties["wheelchair"] = "no";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
delete atm.accessibility
|
||||
|
||||
if (atm.deposit_cash) {
|
||||
properties["cash_in"] = atm.deposit_cash === "1" ? "yes" : "no"
|
||||
delete atm.deposit_cash
|
||||
}
|
||||
|
||||
if (!weekdays.some(wd => atm.regular_hours[wd] !== "00:00-00:00")) {
|
||||
properties["opening_hours"] = "24/7"
|
||||
delete atm.regular_hours
|
||||
} else {
|
||||
const rules = weekdays.filter(wd => atm.regular_hours[wd] !== undefined).map(wd => wd[0] + wd.toLowerCase()[1] + " " + atm.regular_hours[wd]).join(";")
|
||||
properties["opening_hours"] = OH.ToString(OH.MergeTimes(OH.Parse(rules)))
|
||||
delete atm.regular_hours
|
||||
}
|
||||
|
||||
delete atm.special_hours // Only one data point has this
|
||||
|
||||
|
||||
delete atm.location_language
|
||||
delete atm.location_name
|
||||
delete atm.shop_code
|
||||
delete atm.id
|
||||
delete atm.adr_longitude
|
||||
delete atm.adr_latitude
|
||||
delete atm.adr_street_number
|
||||
delete atm.adr_street
|
||||
delete atm.adr_zipcode
|
||||
delete atm.adr_city
|
||||
delete atm.adr_country
|
||||
delete atm.phone_number
|
||||
if (Object.keys(atm).length == 0) {
|
||||
continue
|
||||
}
|
||||
console.log(atm, properties)
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
fs.writeFileSync("atms.geojson", JSON.stringify({type: "FeatureCollection", features}))
|
16
test.ts
16
test.ts
|
@ -3,9 +3,6 @@ import * as theme from "./assets/generated/themes/bookcases.json"
|
|||
import ThemeViewState from "./Models/ThemeViewState"
|
||||
import Combine from "./UI/Base/Combine"
|
||||
import SpecialVisualizations from "./UI/SpecialVisualizations"
|
||||
import {VariableUiElement} from "./UI/Base/VariableUIElement"
|
||||
import {SvgToPdf} from "./Utils/svgToPdf"
|
||||
import {Utils} from "./Utils"
|
||||
|
||||
function testspecial() {
|
||||
const layout = new LayoutConfig(<any>theme, true) // qp.data === "" ? : new AllKnownLayoutsLazy().get(qp.data)
|
||||
|
@ -18,19 +15,6 @@ function testspecial() {
|
|||
}
|
||||
|
||||
|
||||
async function testPdf() {
|
||||
const svgs = await Promise.all(
|
||||
SvgToPdf.templates["flyer_a4"].pages.map((url) => Utils.download(url))
|
||||
)
|
||||
console.log("Building svg")
|
||||
const pdf = new SvgToPdf("Test", svgs, {
|
||||
freeComponentId:"extradiv"
|
||||
})
|
||||
new VariableUiElement(pdf.status).AttachTo("maindiv")
|
||||
await pdf.ExportPdf("nl")
|
||||
}
|
||||
|
||||
testPdf().then((_) => console.log("All done"))
|
||||
/*/
|
||||
testspecial()
|
||||
//*/
|
||||
|
|
Loading…
Reference in a new issue