polylines
This commit is contained in:
parent
72d26191fa
commit
a998e4b244
|
@ -4,6 +4,7 @@ import ./point
|
||||||
import ./operator
|
import ./operator
|
||||||
import ./journey
|
import ./journey
|
||||||
import ./line
|
import ./line
|
||||||
|
import ./polyline
|
||||||
import json
|
import json
|
||||||
import sequtils
|
import sequtils
|
||||||
import strutils
|
import strutils
|
||||||
|
@ -13,8 +14,9 @@ proc parseJourneysResponse*(data: JsonNode, isRefresh: bool = false): JourneysRe
|
||||||
let operators = map(data["res"]["common"]["opL"].getElems(), parseOperator)
|
let operators = map(data["res"]["common"]["opL"].getElems(), parseOperator)
|
||||||
let remarks = map(data["res"]["common"]["remL"].getElems(), parseRemark)
|
let remarks = map(data["res"]["common"]["remL"].getElems(), parseRemark)
|
||||||
let lines = data["res"]["common"]["prodL"]
|
let lines = data["res"]["common"]["prodL"]
|
||||||
|
let polylines = map(data["res"]["common"]["polyL"].getElems(), mkParsePolyline(points))
|
||||||
let timestamp = parseInt(data["res"]["planrtTS"].getStr())
|
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))
|
result.journeys = data["res"]["outConL"].getElems().map(mkParseJourney(common))
|
||||||
if not isRefresh:
|
if not isRefresh:
|
||||||
|
|
|
@ -27,6 +27,14 @@ proc parseLegPart(common: CommonData, lp: JsonNode): LegPart =
|
||||||
proc mkParseLeg*(common: CommonData): proc =
|
proc mkParseLeg*(common: CommonData): proc =
|
||||||
proc parseLeg(l: JsonNode): Leg =
|
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()
|
let typeStr = l{"type"}.getStr()
|
||||||
if typeStr == "JNY":
|
if typeStr == "JNY":
|
||||||
result.direction = some(l{"jny"}{"dirTxt"}.getStr())
|
result.direction = some(l{"jny"}{"dirTxt"}.getStr())
|
||||||
|
|
74
src/backend/hafas/parse/polyline.nim
Normal file
74
src/backend/hafas/parse/polyline.nim
Normal file
|
@ -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
|
|
@ -9,6 +9,7 @@ type
|
||||||
remarks*: seq[Remark]
|
remarks*: seq[Remark]
|
||||||
operators*: seq[Operator]
|
operators*: seq[Operator]
|
||||||
points*: seq[Point]
|
points*: seq[Point]
|
||||||
|
polylines*: seq[Polyline]
|
||||||
dateStr*: string
|
dateStr*: string
|
||||||
timestamp*: int64
|
timestamp*: int64
|
||||||
|
|
||||||
|
@ -42,3 +43,11 @@ type
|
||||||
addName*: Option[string]
|
addName*: Option[string]
|
||||||
opX*: Option[int]
|
opX*: Option[int]
|
||||||
prodCtx*: HafasProdCtx
|
prodCtx*: HafasProdCtx
|
||||||
|
|
||||||
|
HafasLocRef* = object
|
||||||
|
ppIdx*: int
|
||||||
|
locX*: int
|
||||||
|
|
||||||
|
HafasPolyline* = object
|
||||||
|
crdEncYX*: string
|
||||||
|
ppLocRefL*: Option[seq[HafasLocRef]]
|
||||||
|
|
|
@ -69,6 +69,7 @@ type
|
||||||
cancelled*: bool
|
cancelled*: bool
|
||||||
departure*: LegPart
|
departure*: LegPart
|
||||||
arrival*: LegPart
|
arrival*: LegPart
|
||||||
|
polyline*: Polyline
|
||||||
distance*: Option[int] # required for isWalking or isTransfer
|
distance*: Option[int] # required for isWalking or isTransfer
|
||||||
tripId*: Option[string] # required for not isWalking and not isTranfer
|
tripId*: Option[string] # required for not isWalking and not isTranfer
|
||||||
line*: Option[Line] # required for not isWalking and not isTranfer
|
line*: Option[Line] # required for not isWalking and not isTranfer
|
||||||
|
@ -169,26 +170,13 @@ type
|
||||||
|
|
||||||
Feature* = object
|
Feature* = object
|
||||||
`type`*: string
|
`type`*: string
|
||||||
properties*: FeatureProperties
|
properties*: Option[Stop]
|
||||||
geometry*: FeatureGeometry
|
geometry*: FeatureGeometry
|
||||||
|
|
||||||
FeatureProperties* = object
|
|
||||||
`type`*: string
|
|
||||||
id*: int
|
|
||||||
name*: string
|
|
||||||
location*: FeatureLocation
|
|
||||||
products*: Products
|
|
||||||
station*: Station
|
|
||||||
|
|
||||||
FeatureGeometry* = object
|
FeatureGeometry* = object
|
||||||
`type`*: string
|
`type`*: string
|
||||||
coordinates*: seq[float]
|
coordinates*: seq[float]
|
||||||
|
|
||||||
FeatureLocation* = object
|
|
||||||
`type`*: string
|
|
||||||
latitiude*: float
|
|
||||||
longitude*: float
|
|
||||||
|
|
||||||
|
|
||||||
notFoundException* = object of Exception
|
notFoundException* = object of Exception
|
||||||
errorException* = object of Exception
|
errorException* = object of Exception
|
||||||
|
|
Loading…
Reference in a new issue