Skip to content

Commit 5d646f1

Browse files
authored
Merge pull request #37 from LdDl/wkt
Switch geometry import format from GeoJSON to WKT
2 parents 929442b + a659048 commit 5d646f1

File tree

7 files changed

+84839
-84763
lines changed

7 files changed

+84839
-84763
lines changed

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Horizon v0.9.0 [![GoDoc](https://godoc.org/github.com/LdDl/horizon?status.svg)](https://godoc.org/github.com/LdDl/horizon) [![Build Status](https://travis-ci.com/LdDl/horizon.svg?branch=master)](https://travis-ci.com/LdDl/horizon) [![Sourcegraph](https://sourcegraph.com/github.com/LdDl/horizon/-/badge.svg)](https://sourcegraph.com/github.com/LdDl/horizon?badge) [![Go Report Card](https://goreportcard.com/badge/github.com/LdDl/horizon)](https://goreportcard.com/report/github.com/LdDl/horizon) [![GitHub tag](https://img.shields.io/github/tag/LdDl/horizon.svg)](https://github.com/LdDl/horizon/releases)
1+
# Horizon v0.10.0 [![GoDoc](https://godoc.org/github.com/LdDl/horizon?status.svg)](https://godoc.org/github.com/LdDl/horizon) [![Build Status](https://travis-ci.com/LdDl/horizon.svg?branch=master)](https://travis-ci.com/LdDl/horizon) [![Sourcegraph](https://sourcegraph.com/github.com/LdDl/horizon/-/badge.svg)](https://sourcegraph.com/github.com/LdDl/horizon?badge) [![Go Report Card](https://goreportcard.com/badge/github.com/LdDl/horizon)](https://goreportcard.com/report/github.com/LdDl/horizon) [![GitHub tag](https://img.shields.io/github/tag/LdDl/horizon.svg)](https://github.com/LdDl/horizon/releases)
22

33
# Work in progress
44
Horizon is project aimed to do map matching (snap GPS data to map) and routing (find shortest path between two points)
@@ -25,12 +25,12 @@ Demonstration:
2525
Via _go get_:
2626
```shell
2727
go get github.com/LdDl/horizon
28-
go install github.com/LdDl/horizon/cmd/horizon@v0.9.0
28+
go install github.com/LdDl/horizon/cmd/horizon@v0.10.0
2929
```
3030

3131
Via downloading prebuilt binary and making updates in yours PATH environment varibale (both Linux and Windows):
32-
* Windows - https://github.com/LdDl/horizon/releases/download/v0.9.0/windows-horizon.zip
33-
* Linux - https://github.com/LdDl/horizon/releases/download/v0.9.0/linux-amd64-horizon.tar.gz
32+
* Windows - https://github.com/LdDl/horizon/releases/download/v0.10.0/windows-horizon.zip
33+
* Linux - https://github.com/LdDl/horizon/releases/download/v0.10.0/linux-amd64-horizon.tar.gz
3434

3535
Check if **horizon** binary was installed properly:
3636
```shell
@@ -87,11 +87,11 @@ Instruction has been made for Linux mainly. For Windows or OSX the way may vary.
8787
3. Convert *.osm.pbf to CSV via [osm2ch](https://github.com/LdDl/osm2ch#osm2ch).
8888
8989
Notice:
90-
* osm2ch's default output geometry format is WKT and units is 'km' (kilometers). We are going to change those default values. We are going to extract only edges adapted for cars also.
90+
* osm2ch's default output geometry format is WKT and units is 'km' (kilometers). We are going to change units to meters. We are going to extract only edges adapted for cars also.
9191
* Don't forget to prepare contraction hierarchies via flag 'contract=true'
9292
9393
```shell
94-
osm2ch --file map.osm.pbf --out map.csv --geomf geojson --units m --tags motorway,primary,primary_link,road,secondary,secondary_link,residential,tertiary,tertiary_link,unclassified,trunk,trunk_link --contract=true
94+
osm2ch --file map.osm.pbf --out map.csv --geomf wkt --units m --tags motorway,primary,primary_link,road,secondary,secondary_link,residential,tertiary,tertiary_link,unclassified,trunk,trunk_link --contract=true
9595
```
9696
<img src="images/inst6.png" width="720">
9797

map_engine.go

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111

1212
"github.com/LdDl/ch"
1313
"github.com/LdDl/horizon/spatial"
14-
geojson "github.com/paulmach/go.geojson"
1514
"github.com/pkg/errors"
1615
)
1716

@@ -183,14 +182,9 @@ func (engine *MapEngine) extractDataFromCSVs(edgesFname, verticesFname, shortcut
183182
}
184183

185184
coordinates := record[3]
186-
bytesCoordinates := []byte(coordinates)
187-
geojsonPolyline, err := geojson.UnmarshalGeometry(bytesCoordinates)
185+
s2Polyline, err := spatial.WKTToS2PolylineFeature(coordinates)
188186
if err != nil {
189-
return errors.Wrap(err, fmt.Sprintf("Can't parse GeoJSON geometry of the edge: from_vertex_id = '%d' | to_vertex_id = '%d' | geom = '%s'", sourceVertex, targetVertex, coordinates))
190-
}
191-
s2Polyline, err := spatial.GeoJSONToS2PolylineFeature(geojsonPolyline)
192-
if err != nil {
193-
return errors.Wrap(err, fmt.Sprintf("Can't prepare s2-polyline edge: from_vertex_id = '%d' | to_vertex_id = '%d' | geom = '%s'", sourceVertex, targetVertex, coordinates))
187+
return errors.Wrap(err, fmt.Sprintf("Can't parse WKT geometry of the edge: from_vertex_id = '%d' | to_vertex_id = '%d' | geom = '%s'", sourceVertex, targetVertex, coordinates))
194188
}
195189
if _, ok := engine.edges[sourceVertex]; !ok {
196190
engine.edges[sourceVertex] = make(map[int64]*spatial.Edge)
@@ -252,13 +246,9 @@ func (engine *MapEngine) extractDataFromCSVs(edgesFname, verticesFname, shortcut
252246
engine.graph.Vertices[vertexInternal].SetImportance(vertexImportance)
253247

254248
coordinates := record[3]
255-
geoJSONPoint, err := geojson.UnmarshalGeometry([]byte(coordinates))
256-
if err != nil {
257-
return errors.Wrap(err, fmt.Sprintf("Can't parse GeoJSON geometry of the vertex '%d' | geom = '%s'", vertexExternal, coordinates))
258-
}
259-
s2Point, err := spatial.GeoJSONToS2PointFeature(geoJSONPoint)
249+
s2Point, err := spatial.WKTToS2PointFeature(coordinates)
260250
if err != nil {
261-
return errors.Wrap(err, fmt.Sprintf("Can't prepare s2-point vertex '%d' | geom = '%s'", vertexExternal, coordinates))
251+
return errors.Wrap(err, fmt.Sprintf("Can't parse WKT geometry of the vertex '%d' | geom = '%s'", vertexExternal, coordinates))
262252
}
263253
engine.vertices[vertexExternal] = &spatial.Vertex{
264254
ID: vertexExternal,

spatial/utils.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package spatial
33
import (
44
"fmt"
55
"math"
6+
"strconv"
7+
"strings"
68

79
"github.com/golang/geo/s2"
810
geojson "github.com/paulmach/go.geojson"
@@ -145,6 +147,90 @@ func S2PointToGeoJSONFeature(pt *s2.Point) *geojson.Feature {
145147
return geojson.NewPointFeature([]float64{latLng.Lng.Degrees(), latLng.Lat.Degrees()})
146148
}
147149

150+
// WKTToS2PolylineFeature parses WKT LINESTRING and returns *s2.Polyline
151+
// Expected format: LINESTRING(lon1 lat1, lon2 lat2, ...)
152+
func WKTToS2PolylineFeature(wkt string) (*s2.Polyline, error) {
153+
wkt = strings.TrimSpace(wkt)
154+
155+
// Check for LINESTRING prefix
156+
if !strings.HasPrefix(strings.ToUpper(wkt), "LINESTRING") {
157+
return nil, fmt.Errorf("expected LINESTRING, got: %s", wkt)
158+
}
159+
160+
// Extract coordinates between parentheses
161+
start := strings.Index(wkt, "(")
162+
end := strings.LastIndex(wkt, ")")
163+
if start == -1 || end == -1 || start >= end {
164+
return nil, fmt.Errorf("invalid WKT LINESTRING format: %s", wkt)
165+
}
166+
167+
coordsStr := wkt[start+1 : end]
168+
pointStrs := strings.Split(coordsStr, ",")
169+
170+
latLngs := make([]s2.LatLng, 0, len(pointStrs))
171+
for _, ptStr := range pointStrs {
172+
ptStr = strings.TrimSpace(ptStr)
173+
parts := strings.Fields(ptStr)
174+
if len(parts) < 2 {
175+
return nil, fmt.Errorf("invalid coordinate pair: %s", ptStr)
176+
}
177+
178+
lon, err := strconv.ParseFloat(parts[0], 64)
179+
if err != nil {
180+
return nil, fmt.Errorf("invalid longitude: %s", parts[0])
181+
}
182+
183+
lat, err := strconv.ParseFloat(parts[1], 64)
184+
if err != nil {
185+
return nil, fmt.Errorf("invalid latitude: %s", parts[1])
186+
}
187+
188+
latLngs = append(latLngs, s2.LatLngFromDegrees(lat, lon))
189+
}
190+
191+
if len(latLngs) < 2 {
192+
return nil, fmt.Errorf("LINESTRING must have at least 2 points")
193+
}
194+
195+
return s2.PolylineFromLatLngs(latLngs), nil
196+
}
197+
198+
// WKTToS2PointFeature parses WKT POINT and returns s2.Point
199+
// Expected format: POINT(lon lat)
200+
func WKTToS2PointFeature(wkt string) (s2.Point, error) {
201+
wkt = strings.TrimSpace(wkt)
202+
203+
// Check for POINT prefix
204+
if !strings.HasPrefix(strings.ToUpper(wkt), "POINT") {
205+
return s2.Point{}, fmt.Errorf("expected POINT, got: %s", wkt)
206+
}
207+
208+
// Extract coordinates between parentheses
209+
start := strings.Index(wkt, "(")
210+
end := strings.LastIndex(wkt, ")")
211+
if start == -1 || end == -1 || start >= end {
212+
return s2.Point{}, fmt.Errorf("invalid WKT POINT format: %s", wkt)
213+
}
214+
215+
coordsStr := strings.TrimSpace(wkt[start+1 : end])
216+
parts := strings.Fields(coordsStr)
217+
if len(parts) < 2 {
218+
return s2.Point{}, fmt.Errorf("invalid coordinate pair: %s", coordsStr)
219+
}
220+
221+
lon, err := strconv.ParseFloat(parts[0], 64)
222+
if err != nil {
223+
return s2.Point{}, fmt.Errorf("invalid longitude: %s", parts[0])
224+
}
225+
226+
lat, err := strconv.ParseFloat(parts[1], 64)
227+
if err != nil {
228+
return s2.Point{}, fmt.Errorf("invalid latitude: %s", parts[1])
229+
}
230+
231+
return s2.PointFromLatLng(s2.LatLngFromDegrees(lat, lon)), nil
232+
}
233+
148234
// ExtractCutUpTo cuts geometry between very first point and neighbor of the projected point index in the polyline
149235
func ExtractCutUpTo(polyline s2.Polyline, projected s2.Point, projectedIdx int) (s2.Polyline, s2.Polyline) {
150236
polyCopy := polyline

test_data/matcher_4326_test.csv

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from_vertex_id;to_vertex_id;weight;geom;was_one_way;edge_id
2-
101;102;131.30390541936353;"{""type"":""LineString"",""coordinates"":[[37.66309622043991,55.773079419469],[37.66313666122084,55.77327959082814],[37.66310430859609,55.773520704964014],[37.66309622043992,55.773934689906994],[37.66309167085209,55.774258254747494]]}";true;1
3-
102;103;91.47099061865808;"{""type"":""LineString"",""coordinates"":[[37.66309167085209,55.774258254747494],[37.66374731701271,55.77424602870825],[37.66423665046183,55.77426877482456],[37.664552088552995,55.7742642256024]]}";true;2
4-
102;105;91.0490951658074;"{""type"":""LineString"",""coordinates"":[[37.66309167085209,55.774258254747494],[37.66305982373708,55.77454627637461],[37.66307195597136,55.7747282435316],[37.66314474937702,55.7750739787893]]}";true;3
5-
103;106;93.3306443622582;"{""type"":""LineString"",""coordinates"":[[37.664552088552995,55.7742642256024],[37.66448333922546,55.77441434965456],[37.66457635302158,55.77458721905901],[37.664560176709216,55.774698673926395],[37.664544000396845,55.77488291494706],[37.66460061749012,55.7750307620498],[37.6645156918502,55.77506033140307]]}";true;4
6-
104;103;171.32733035056765;"{""type"":""LineString"",""coordinates"":[[37.66434988464841,55.77279479904767],[37.664544000396816,55.77293583000571],[37.66464105827104,55.77308140981424],[37.664657234583395,55.77327703182576],[37.66477046876997,55.77346810354034],[37.66476238061379,55.773713765797055],[37.66468149905196,55.77387299050682],[37.664624881958666,55.77409590400769],[37.664552088552995,55.7742642256024]]}";true;5
2+
101;102;131.30390541936353;LINESTRING(37.66309622043991 55.773079419469, 37.66313666122084 55.77327959082814, 37.66310430859609 55.773520704964014, 37.66309622043992 55.773934689906994, 37.66309167085209 55.774258254747494);true;1
3+
102;103;91.47099061865808;LINESTRING(37.66309167085209 55.774258254747494, 37.66374731701271 55.77424602870825, 37.66423665046183 55.77426877482456, 37.664552088552995 55.7742642256024);true;2
4+
102;105;91.0490951658074;LINESTRING(37.66309167085209 55.774258254747494, 37.66305982373708 55.77454627637461, 37.66307195597136 55.7747282435316, 37.66314474937702 55.7750739787893);true;3
5+
103;106;93.3306443622582;LINESTRING(37.664552088552995 55.7742642256024, 37.66448333922546 55.77441434965456, 37.66457635302158 55.77458721905901, 37.664560176709216 55.774698673926395, 37.664544000396845 55.77488291494706, 37.66460061749012 55.7750307620498, 37.6645156918502 55.77506033140307);true;4
6+
104;103;171.32733035056765;LINESTRING(37.66434988464841 55.77279479904767, 37.664544000396816 55.77293583000571, 37.66464105827104 55.77308140981424, 37.664657234583395 55.77327703182576, 37.66477046876997 55.77346810354034, 37.66476238061379 55.773713765797055, 37.66468149905196 55.77387299050682, 37.664624881958666 55.77409590400769, 37.664552088552995 55.7742642256024);true;5
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
vertex_id;order_pos;importance;geom
2-
101;0;11;"{""type"":""Point"",""coordinates"":[37.66309622043991,55.773079419469]}"
3-
102;5;91;"{""type"":""Point"",""coordinates"":[37.66309167085209,55.774258254747494]}"
4-
103;4;81;"{""type"":""Point"",""coordinates"":[37.664552088552995,55.7742642256024]}"
5-
104;1;11;"{""type"":""Point"",""coordinates"":[37.66434988464841,55.77279479904767]}"
6-
105;2;11;"{""type"":""Point"",""coordinates"":[37.66314474937702,55.7750739787893]}"
7-
106;3;11;"{""type"":""Point"",""coordinates"":[37.6645156918502,55.77506033140307]}"
2+
101;0;11;POINT(37.66309622043991 55.773079419469)
3+
102;5;91;POINT(37.66309167085209 55.774258254747494)
4+
103;4;81;POINT(37.664552088552995 55.7742642256024)
5+
104;1;11;POINT(37.66434988464841 55.77279479904767)
6+
105;2;11;POINT(37.66314474937702 55.7750739787893)
7+
106;3;11;POINT(37.6645156918502 55.77506033140307)

0 commit comments

Comments
 (0)