diff --git a/src/backend/hafas/parse/journeys_response.nim b/src/backend/hafas/parse/journeys_response.nim index 8c00c40..a9b16f7 100644 --- a/src/backend/hafas/parse/journeys_response.nim +++ b/src/backend/hafas/parse/journeys_response.nim @@ -4,6 +4,7 @@ import ./point import ./operator import ./journey import ./line +import ./polyline import json import sequtils import strutils @@ -13,8 +14,9 @@ proc parseJourneysResponse*(data: JsonNode, isRefresh: bool = false): JourneysRe let operators = map(data["res"]["common"]["opL"].getElems(), parseOperator) let remarks = map(data["res"]["common"]["remL"].getElems(), parseRemark) let lines = data["res"]["common"]["prodL"] + let polylines = map(data["res"]["common"]["polyL"].getElems(), mkParsePolyline(points)) let timestamp = parseInt(data["res"]["planrtTS"].getStr()) - let common = CommonData(points: points, operators: operators, remarks: remarks, lines: lines, timestamp: timestamp) + let common = CommonData(points: points, operators: operators, remarks: remarks, lines: lines, polylines: polylines, timestamp: timestamp) result.journeys = data["res"]["outConL"].getElems().map(mkParseJourney(common)) if not isRefresh: diff --git a/src/backend/hafas/parse/leg.nim b/src/backend/hafas/parse/leg.nim index 08652f9..16816e7 100644 --- a/src/backend/hafas/parse/leg.nim +++ b/src/backend/hafas/parse/leg.nim @@ -27,6 +27,14 @@ proc parseLegPart(common: CommonData, lp: JsonNode): LegPart = proc mkParseLeg*(common: CommonData): proc = proc parseLeg(l: JsonNode): Leg = + if l{"polyG"}{"polyX"}.getElems().len() > 0: + result.polyline = Polyline( + type: "FeatureCollection" + ) + for n in l{"polyG"}{"polyX"}.getElems(): + result.polyline.features &= common.polylines[n.getInt()].features + echo pretty(%result.polyline) + let typeStr = l{"type"}.getStr() if typeStr == "JNY": result.direction = some(l{"jny"}{"dirTxt"}.getStr()) diff --git a/src/backend/hafas/parse/polyline.nim b/src/backend/hafas/parse/polyline.nim new file mode 100644 index 0000000..7f70e72 --- /dev/null +++ b/src/backend/hafas/parse/polyline.nim @@ -0,0 +1,74 @@ +import ../types +import ../util +import json +import options +import math + +proc gpsDistance(fromLat: float, fromLon: float, toLat: float, toLon: float): float = + proc toRad(x: float): float = x * PI / 180 + let dLat = toRad(toLat - fromLat) + let dLon = toRad(toLon - fromLon) + let fromLat = toRad(fromLat) + let toLat = toRad(toLat) + let a = pow(sin(dLat / 2), 2) + (pow(sin(dLon / 2), 2) * cos(fromLat) * cos(toLat)) + let c = 2 * arctan2(sqrt(a), sqrt(1 - a)) + return 6371 * c + +proc parseIntegers(str: string): seq[int] = + var byte = 0 + var current = 0 + var bits = 0 + for c in str: + byte = int(c) - 63 + current = current or (( byte and 31 ) shl bits) + bits += 5 + + if byte < 32: + if (current and 1) == 1: + current = -current + current = current shr 1 + + result.add(current) + + current = 0 + bits = 0 + +proc mkParsePolyline*(points: seq[Point]): proc = + proc parsePolyline(l: JsonNode): Polyline = + let line = l.to(HafasPolyLine) + + result.type = "FeatureCollection" + + var lat = 0 + var lon = 0 + + let ints = parseIntegers(line.crdEncYX) + var i = 0 + while i < len(ints): + lat += ints[i] + lon += ints[i+1] + result.features.add(Feature( + type: "Feature", + geometry: FeatureGeometry( + type: "Point", + coordinates: @[lon / 100000, lat / 100000], + ), + )) + i += 2 + + if line.ppLocRefL.isSome: + for p in line.ppLocRefL.get: + result.features[p.ppIdx].properties = points[p.locX].stop + + # sort out coordinates closer than 5m to their neighbours + var j = 1 + while true: + if j >= len(result.features): break + let last = result.features[j-1].geometry.coordinates + let current = result.features[j].geometry.coordinates + if gpsDistance(last[1], last[0], current[1], current[0]) <= 0.005: + result.features.delete(j) + continue + j += 1 + + return parsePolyline diff --git a/src/backend/hafas/types.nim b/src/backend/hafas/types.nim index 126f0bc..a12f34b 100644 --- a/src/backend/hafas/types.nim +++ b/src/backend/hafas/types.nim @@ -9,6 +9,7 @@ type remarks*: seq[Remark] operators*: seq[Operator] points*: seq[Point] + polylines*: seq[Polyline] dateStr*: string timestamp*: int64 @@ -42,3 +43,11 @@ type addName*: Option[string] opX*: Option[int] prodCtx*: HafasProdCtx + + HafasLocRef* = object + ppIdx*: int + locX*: int + + HafasPolyline* = object + crdEncYX*: string + ppLocRefL*: Option[seq[HafasLocRef]] diff --git a/src/types.nim b/src/types.nim index b0094e7..818409c 100644 --- a/src/types.nim +++ b/src/types.nim @@ -69,6 +69,7 @@ type cancelled*: bool departure*: LegPart arrival*: LegPart + polyline*: Polyline distance*: Option[int] # required for isWalking or isTransfer tripId*: Option[string] # required for not isWalking and not isTranfer line*: Option[Line] # required for not isWalking and not isTranfer @@ -169,26 +170,13 @@ type Feature* = object `type`*: string - properties*: FeatureProperties + properties*: Option[Stop] geometry*: FeatureGeometry - FeatureProperties* = object - `type`*: string - id*: int - name*: string - location*: FeatureLocation - products*: Products - station*: Station - FeatureGeometry* = object `type`*: string coordinates*: seq[float] - FeatureLocation* = object - `type`*: string - latitiude*: float - longitude*: float - notFoundException* = object of Exception errorException* = object of Exception