ctucx.git: oeffisearch

[nimlang] fast and simple tripplanner

commit a998e4b244bc5aab1c6ee4398c9d23b25310d170
parent 72d26191faa8cd707f5d98ec8722d76e0126c07a
Author: Milan Pässler <me@pbb.lc>
Date: Fri, 14 Feb 2020 16:34:52 +0100

polylines
5 files changed, 96 insertions(+), 15 deletions(-)
M
src/backend/hafas/parse/journeys_response.nim
|
4
+++-
M
src/backend/hafas/parse/leg.nim
|
8
++++++++
A
src/backend/hafas/parse/polyline.nim
|
74
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
M
src/backend/hafas/types.nim
|
9
+++++++++
M
src/types.nim
|
16
++--------------
diff --git 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
@@ -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
@@ -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
@@ -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
@@ -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