Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support new scheme tagging #83

Merged
merged 4 commits into from
Mar 17, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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',
]
Copy link
Contributor

@jakecoppinger jakecoppinger Mar 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should free; residents be added?

In Australia, a common scenario is:

  • Free parking
  • Time limited for public to a few hours
  • Unlimited time for residents (permit holders).

I believe this is covered by the tagging guidelines like so:

parking:condition:right=ticket; residents (This is a paid parking with no other restrictions. Residents may also park here with a permit.)
parking:condition:right:conditional=no_parking @ (Oct 1-Apr 30: Mo 10:00-15:00) (You may not park here on Mondays 10-15 during October-April.)
parking:condition:right:residents=Ku (Residents need permit "Ta".)

(Taken from second example on https://wiki.openstreetmap.org/wiki/Key:parking:condition/complex)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It too future task


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(
}

export 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 @@ -89,7 +89,7 @@ function getDefaultCondition(tags: OsmTags): 'yes' | 'ticket' | 'free' | 'custom
}
}

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 @@ -118,14 +118,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