Skip to content

Commit

Permalink
Implement editing conditional tag
Browse files Browse the repository at this point in the history
  • Loading branch information
zlant committed Mar 13, 2022
1 parent 9736465 commit a9f3b43
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 92 deletions.
115 changes: 99 additions & 16 deletions src/parking/controls/editor/editor-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { WaysInRelation } from '../../../utils/types/osm-data-storage'
import { OsmKeyValue } from '../../../utils/types/preset'
import { presets } from './presets'
import { getAllTagsBlock } from '../lane-info'
import { parseConditionalTag, ConditionalValue } from '../../../utils/conditional-tag'

export function getLaneEditForm(osm: OsmWay, waysInRelation: WaysInRelation, cutLaneListener: (way: OsmWay) => void): HTMLFormElement {
const form = hyper`
Expand Down Expand Up @@ -91,6 +92,7 @@ const parkingLaneTagTemplates = [
'parking:condition:{side}',
'parking:condition:{side}:time_interval',
'parking:condition:{side}:default',
'parking:condition:{side}:conditional',
'parking:condition:{side}:maxstay',
'parking:lane:{side}:capacity',
'parking:lane:{side}:surface',
Expand All @@ -112,6 +114,13 @@ function getTagInput(osm: OsmWay, side: string, parkingType: string, tagTemplate
const tagSplit = tag.split(':')
const label = tagSplit[Math.floor(tagSplit.length / 2) * 2 - 1]

if (tagTemplate === 'parking:condition:{side}:conditional') {
const conditionTag = 'parking:condition:{side}'
.replace('{side}', side)
const hide = !osm.tags[conditionTag]
return getConditionalInput(osm, tag, label, hide)
}

const value = osm.tags[tag]

let input: HTMLInputElement | HTMLSelectElement
Expand All @@ -126,6 +135,7 @@ function getTagInput(osm: OsmWay, side: string, parkingType: string, tagTemplate
case 'parking:condition:{side}:time_interval':
input = getTextInput(tag, value)
input.oninput = handleTimeIntervalTagInput
hide = !osm.tags[tagTemplate.replace('{side}', side)]
break

case 'parking:lane:{side}:{type}': {
Expand Down Expand Up @@ -169,14 +179,7 @@ function getTagInput(osm: OsmWay, side: string, parkingType: string, tagTemplate
break
}

input.onchange = (e) => {
if (!(e.currentTarget instanceof HTMLInputElement || e.currentTarget instanceof HTMLSelectElement) ||
e.currentTarget.form == null)
return

const newOsm = formToOsmWay(osm, e.currentTarget.form)
osmChangeListener?.(newOsm)
}
input.onchange = (e) => handleInputChange(e, osm)

return hyper`
<tr id="${tag}"
Expand All @@ -188,25 +191,76 @@ function getTagInput(osm: OsmWay, side: string, parkingType: string, tagTemplate
</tr>` as HTMLElement
}

function handleInputChange(e: Event, osm: OsmWay) {
if (!(e.currentTarget instanceof HTMLInputElement || e.currentTarget instanceof HTMLSelectElement) ||
e.currentTarget.form == null)
return

const newOsm = formToOsmWay(osm, e.currentTarget.form)
osmChangeListener?.(newOsm)
}

function getSelectInput(tag: string, value: string, values: string[]): HTMLSelectElement {
const options = !value || values.includes(value) ?
['', ...values] :
['', value, ...values]

return hyper`
<select name=${tag}>
<select name=${tag}
class="editor-form__select-input">
${options.map(o => hyper`<option value=${o} selected=${value === o}>${o}</option>`)}
</select>`
}

function getTextInput(tag: string, value: string): HTMLInputElement {
return hyper`
<input type="text"
class="editor-form__text-input"
placeholder="${tag}"
name="${tag}"
value="${value ?? ''}">`
}

function getConditionalInput(osm: OsmWay, tag: string, label: string, hide: boolean): HTMLElement {
const parsedConditionalTag = osm.tags[tag] ? parseConditionalTag(osm.tags[tag]) : []
parsedConditionalTag.push({ value: '', condition: null })

return hyper`
<tr id="${tag}"
class="conditional-tag"
style=${{ display: hide ? 'none' : null }}>
<td colspan="2">
<table>
<tr><td><label title="${tag}">${label}</label></td></tr>
${parsedConditionalTag.map((conditionalValue, i) => getConditionalPartInput(osm, tag, conditionalValue, i))}
</table>
</td>
</tr>`
}

function getConditionalPartInput(osm: OsmWay, tag: string, part: ConditionalValue, partindex: number) {
const selectInput = getSelectInput(`${tag}`, part.value, conditionValues)
selectInput.onchange = (e) => handleInputChange(e, osm)
selectInput.dataset.partindex = partindex.toString()
selectInput.dataset.tokenname = 'condition'

return hyper`
<tr>
<td>
${selectInput}
</td>
<td>
<input type="text"
placeholder="time interval"
name="${tag}"
value="${part.condition}"
data-partindex="${partindex}"
data-tokenname="time_interval"
oninput=${(e) => handleInputChange(e, osm)}>
</td>
</tr>`
}

function getPresetSigns(osm: OsmWay, side: 'both'|'left'|'right') {
return presets.map(x => hyper`
<img src=${x.img.src}
Expand Down Expand Up @@ -279,6 +333,10 @@ function handleConditionTagInput(e: Event) {
hideElement(maxstayTr!.id)
else
showElement(maxstayTr!.id)

const condtionalBlockId = `parking:condition:${side}:conditional`
if (el.value)
showElement(condtionalBlockId)
}

function handleTimeIntervalTagInput(e: Event) {
Expand Down Expand Up @@ -306,11 +364,9 @@ const laneValues = [
'parallel',
'diagonal',
'perpendicular',
'no_parking',
'no_stopping',
'marked',
'fire_lane',
'no',
'yes',
'separate',
]

Expand All @@ -330,6 +386,11 @@ const conditionValues = [
'residents',
'customers',
'private',
'disabled',
'no_parking',
'no_standing',
'no_stopping',
'no',
]

let osmChangeListener: (way: OsmWay) => void
Expand All @@ -339,7 +400,7 @@ export function setOsmChangeListener(listener: (way: OsmWay) => void) {
}

function formToOsmWay(osm: OsmWay, form: HTMLFormElement) {
const regex = /^parking:/
const regex = /^parking:(?!>conditional$)/

const supprtedTags = parkingLaneTagTemplates
.map(x => {
Expand All @@ -354,10 +415,32 @@ function formToOsmWay(osm: OsmWay, form: HTMLFormElement) {
delete osm.tags[tagKey]
}

const conditionals: {[tag: string]: string[][]} = {}

for (const input of Array.from(form.elements)) {
if ((input instanceof HTMLInputElement || input instanceof HTMLSelectElement) &&
regex.test(input.name) && input.value)
osm.tags[input.name] = input.value
if (input instanceof HTMLInputElement || input instanceof HTMLSelectElement) {
if (regex.test(input.name) && input.value)
osm.tags[input.name] = input.value

if (input.dataset.partindex) {
if (!conditionals[input.name])
conditionals[input.name] = []

if (conditionals[input.name].length < parseInt(input.dataset.partindex) + 1)
conditionals[input.name].push(['', ''])

conditionals[input.name][parseInt(input.dataset.partindex)][input.dataset.tokenname === 'condition' ? 0 : 1] = input.value
}
}
}

for (const conditionalTag in conditionals) {
if (conditionals[conditionalTag].length > 0 && conditionals[conditionalTag][0][0]) {
osm.tags[conditionalTag] = conditionals[conditionalTag]
.filter(x => x[0])
.map(x => x[1] ? `${x[0]} @ (${x[1]})` : x[0])
.join('; ')
}
}

return osm
Expand Down
2 changes: 1 addition & 1 deletion src/parking/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { ParkingAreas, ParkingLanes } from '../utils/types/parking'
import { parseParkingArea, updateAreaColorsByDate } from './parking-area'

const editorName = 'PLanes'
const version = '0.6.1'
const version = '0.7.0'

let editorMode = false
const useDevServer = false
Expand Down
34 changes: 17 additions & 17 deletions src/parking/parking-area.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import L from 'leaflet'
import { getOpeningHourseState, parseOpeningHourse } from '../utils/opening-hours'
import { ConditionColor, ConditionsInterface } from '../utils/types/conditions'
import { ConditionColor, ParkingConditions } from '../utils/types/conditions'
import { ParkingPolylineOptions } from '../utils/types/leaflet'
import { OsmTags, OsmWay } from '../utils/types/osm-data'
import { ParkingAreas } from '../utils/types/parking'
Expand All @@ -24,30 +24,30 @@ export function parseParkingArea(
}

function getConditions(tags: OsmTags) {
const conditions: ConditionsInterface = {
intervals: [],
const conditions: ParkingConditions = {
conditionalValues: [],
default: getDefaultCondition(tags),
}

if (tags.opening_hours) {
conditions.intervals!.push({
interval: parseOpeningHourse(tags.opening_hours),
condition: conditions.default!,
conditions.conditionalValues!.push({
condition: parseOpeningHourse(tags.opening_hours),
parkingCondition: conditions.default!,
})
conditions.default = 'no_stopping'
}
if (tags.fee && tags.fee !== 'yes' && tags.fee !== 'no') {
conditions.intervals?.push({
interval: parseOpeningHourse(tags.fee),
condition: 'ticket',
conditions.conditionalValues?.push({
condition: parseOpeningHourse(tags.fee),
parkingCondition: 'ticket',
})
}
if (tags['fee:conditional']) {
const match = tags['fee:conditional'].match(/(?<value>.*?) *@ *\((?<interval>.*?)\)/)
if (match?.groups?.interval) {
conditions.intervals?.push({
interval: parseOpeningHourse(match?.groups?.interval),
condition: match?.groups?.value === 'yes' ? 'ticket' : 'free',
conditions.conditionalValues?.push({
condition: parseOpeningHourse(match?.groups?.interval),
parkingCondition: match?.groups?.value === 'yes' ? 'ticket' : 'free',
})
}
}
Expand Down Expand Up @@ -84,7 +84,7 @@ function getDefaultCondition(tags: OsmTags) {
}
}

function createPolygon(line: L.LatLngLiteral[], conditions: ConditionsInterface | undefined, osm: OsmWay, zoom: number) {
function createPolygon(line: L.LatLngLiteral[], conditions: ParkingConditions | undefined, osm: OsmWay, zoom: number) {
const polylineOptions: ParkingPolylineOptions = {
color: getColor(conditions?.default),
fillOpacity: 0.6,
Expand Down Expand Up @@ -113,14 +113,14 @@ export function updateAreaColorsByDate(areas: ParkingAreas, datetime: Date): voi
}
}

function getColorByDate(conditions: ConditionsInterface, datetime: Date): ConditionColor | undefined {
function getColorByDate(conditions: ParkingConditions, datetime: Date): ConditionColor | undefined {
if (!conditions)
return 'black'

// If conditions.intervals not defined, return the default color
for (const interval of conditions.intervals ?? []) {
if (interval.interval && getOpeningHourseState(interval.interval, datetime))
return getColor(interval.condition)
for (const interval of conditions.conditionalValues ?? []) {
if (interval.condition && getOpeningHourseState(interval.condition, datetime))
return getColor(interval.parkingCondition)
}
return getColor(conditions.default)
}
Loading

0 comments on commit a9f3b43

Please sign in to comment.