2013-05-21 11:50:10 +04:00
|
|
|
package writer
|
|
|
|
|
|
|
|
import (
|
2014-08-11 12:18:08 +04:00
|
|
|
"sync"
|
|
|
|
|
2014-08-04 17:19:35 +04:00
|
|
|
"github.com/omniscale/imposm3/cache"
|
|
|
|
"github.com/omniscale/imposm3/database"
|
|
|
|
"github.com/omniscale/imposm3/element"
|
|
|
|
"github.com/omniscale/imposm3/expire"
|
2015-04-30 10:42:49 +03:00
|
|
|
geomp "github.com/omniscale/imposm3/geom"
|
2014-08-04 17:19:35 +04:00
|
|
|
"github.com/omniscale/imposm3/geom/geos"
|
|
|
|
"github.com/omniscale/imposm3/mapping"
|
|
|
|
"github.com/omniscale/imposm3/stats"
|
2013-05-21 11:50:10 +04:00
|
|
|
)
|
|
|
|
|
|
|
|
type WayWriter struct {
|
2013-05-28 14:54:19 +04:00
|
|
|
OsmElemWriter
|
2014-08-11 12:18:08 +04:00
|
|
|
singleIdSpace bool
|
2014-05-05 12:21:46 +04:00
|
|
|
ways chan *element.Way
|
2014-06-19 13:51:15 +04:00
|
|
|
lineMatcher mapping.WayMatcher
|
|
|
|
polygonMatcher mapping.WayMatcher
|
2014-10-22 15:36:06 +04:00
|
|
|
maxGap float64
|
2013-05-21 11:50:10 +04:00
|
|
|
}
|
|
|
|
|
2014-05-05 12:21:46 +04:00
|
|
|
func NewWayWriter(
|
|
|
|
osmCache *cache.OSMCache,
|
|
|
|
diffCache *cache.DiffCache,
|
2014-08-11 12:18:08 +04:00
|
|
|
singleIdSpace bool,
|
2014-05-05 12:21:46 +04:00
|
|
|
ways chan *element.Way,
|
2013-10-28 14:37:58 +04:00
|
|
|
inserter database.Inserter,
|
2014-05-05 12:21:46 +04:00
|
|
|
progress *stats.Statistics,
|
2014-06-19 13:51:15 +04:00
|
|
|
polygonMatcher mapping.WayMatcher,
|
|
|
|
lineMatcher mapping.WayMatcher,
|
2014-05-05 12:21:46 +04:00
|
|
|
srid int,
|
|
|
|
) *OsmElemWriter {
|
2014-10-22 15:36:06 +04:00
|
|
|
maxGap := 1e-1 // 0.1m
|
|
|
|
if srid == 4326 {
|
|
|
|
maxGap = 1e-6 // ~0.1m
|
|
|
|
}
|
2013-05-21 11:50:10 +04:00
|
|
|
ww := WayWriter{
|
2013-05-28 14:54:19 +04:00
|
|
|
OsmElemWriter: OsmElemWriter{
|
2013-10-28 11:26:51 +04:00
|
|
|
osmCache: osmCache,
|
|
|
|
diffCache: diffCache,
|
|
|
|
progress: progress,
|
|
|
|
wg: &sync.WaitGroup{},
|
|
|
|
inserter: inserter,
|
|
|
|
srid: srid,
|
2013-05-28 14:54:19 +04:00
|
|
|
},
|
2014-08-11 12:18:08 +04:00
|
|
|
singleIdSpace: singleIdSpace,
|
2014-05-05 12:21:46 +04:00
|
|
|
lineMatcher: lineMatcher,
|
|
|
|
polygonMatcher: polygonMatcher,
|
|
|
|
ways: ways,
|
2014-10-22 15:36:06 +04:00
|
|
|
maxGap: maxGap,
|
2013-05-21 11:50:10 +04:00
|
|
|
}
|
2013-05-28 14:54:19 +04:00
|
|
|
ww.OsmElemWriter.writer = &ww
|
|
|
|
return &ww.OsmElemWriter
|
2013-05-21 11:50:10 +04:00
|
|
|
}
|
|
|
|
|
2014-08-11 12:18:08 +04:00
|
|
|
func (ww *WayWriter) wayId(id int64) int64 {
|
|
|
|
if !ww.singleIdSpace {
|
|
|
|
return id
|
|
|
|
}
|
|
|
|
return -id
|
|
|
|
}
|
|
|
|
|
2013-05-21 11:50:10 +04:00
|
|
|
func (ww *WayWriter) loop() {
|
2013-05-24 12:08:38 +04:00
|
|
|
geos := geos.NewGeos()
|
2013-06-19 11:19:21 +04:00
|
|
|
geos.SetHandleSrid(ww.srid)
|
2013-05-21 11:50:10 +04:00
|
|
|
defer geos.Finish()
|
2013-06-21 13:05:29 +04:00
|
|
|
for w := range ww.ways {
|
2013-07-29 17:47:09 +04:00
|
|
|
ww.progress.AddWays(1)
|
2013-07-15 13:41:03 +04:00
|
|
|
if len(w.Tags) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
2013-05-21 11:50:10 +04:00
|
|
|
|
2017-04-03 16:19:01 +03:00
|
|
|
filled := false
|
|
|
|
// fill loads all coords. call only if we have a match
|
|
|
|
fill := func(w *element.Way) bool {
|
|
|
|
if filled {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
err := ww.osmCache.Coords.FillWay(w)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
ww.NodesToSrid(w.Nodes)
|
|
|
|
filled = true
|
|
|
|
return true
|
2013-05-21 11:50:10 +04:00
|
|
|
}
|
2013-07-12 16:57:06 +04:00
|
|
|
|
2014-08-11 12:18:08 +04:00
|
|
|
w.Id = ww.wayId(w.Id)
|
|
|
|
|
2017-11-17 15:56:39 +03:00
|
|
|
var err error
|
2014-01-13 11:52:15 +04:00
|
|
|
inserted := false
|
2016-11-23 16:43:04 +03:00
|
|
|
insertedPolygon := false
|
2014-06-19 13:51:15 +04:00
|
|
|
if matches := ww.lineMatcher.MatchWay(w); len(matches) > 0 {
|
2017-04-03 16:19:01 +03:00
|
|
|
if !fill(w) {
|
|
|
|
continue
|
|
|
|
}
|
2017-02-01 13:59:41 +03:00
|
|
|
err, inserted = ww.buildAndInsert(geos, w, matches, false)
|
2014-04-28 13:43:25 +04:00
|
|
|
if err != nil {
|
|
|
|
if errl, ok := err.(ErrorLevel); !ok || errl.Level() > 0 {
|
|
|
|
log.Warn(err)
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
2013-05-21 11:50:10 +04:00
|
|
|
}
|
2017-11-17 15:56:39 +03:00
|
|
|
if matches := ww.polygonMatcher.MatchWay(w); len(matches) > 0 {
|
|
|
|
if !fill(w) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if w.IsClosed() {
|
|
|
|
err, insertedPolygon = ww.buildAndInsert(geos, w, matches, true)
|
|
|
|
if err != nil {
|
|
|
|
if errl, ok := err.(ErrorLevel); !ok || errl.Level() > 0 {
|
|
|
|
log.Warn(err)
|
2017-04-03 16:19:01 +03:00
|
|
|
}
|
2017-11-17 15:56:39 +03:00
|
|
|
continue
|
2017-04-03 16:19:01 +03:00
|
|
|
}
|
2013-05-21 11:50:10 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-01 13:59:41 +03:00
|
|
|
if (inserted || insertedPolygon) && ww.expireor != nil {
|
2016-11-23 16:43:04 +03:00
|
|
|
expire.ExpireProjectedNodes(ww.expireor, w.Nodes, ww.srid, insertedPolygon)
|
2013-07-15 13:14:03 +04:00
|
|
|
}
|
2017-04-03 16:19:01 +03:00
|
|
|
if (inserted || insertedPolygon) && ww.diffCache != nil {
|
2013-05-30 14:00:11 +04:00
|
|
|
ww.diffCache.Coords.AddFromWay(w)
|
|
|
|
}
|
2013-05-21 11:50:10 +04:00
|
|
|
}
|
|
|
|
ww.wg.Done()
|
|
|
|
}
|
2013-05-28 14:54:19 +04:00
|
|
|
|
2017-11-21 11:28:16 +03:00
|
|
|
func (ww *WayWriter) buildAndInsert(
|
|
|
|
g *geos.Geos,
|
|
|
|
w *element.Way,
|
|
|
|
matches []mapping.Match,
|
|
|
|
isPolygon bool,
|
|
|
|
) (error, bool) {
|
|
|
|
|
2013-05-28 14:54:19 +04:00
|
|
|
// make copy to avoid interference with polygon/linestring matches
|
|
|
|
way := element.Way(*w)
|
2013-10-28 14:28:56 +04:00
|
|
|
|
2017-11-21 11:28:16 +03:00
|
|
|
// Shortcut for non-clipped LineStrings:
|
|
|
|
// We don't need any function from GEOS, so we can directly create the WKB hex string.
|
|
|
|
if ww.limiter == nil && !isPolygon {
|
|
|
|
wkb, err := geomp.NodesAsEWKBHexLineString(w.Nodes, ww.srid)
|
|
|
|
if err != nil {
|
|
|
|
return err, false
|
|
|
|
}
|
|
|
|
geom := geomp.Geometry{Wkb: wkb}
|
|
|
|
if err := ww.inserter.InsertLineString(w.OSMElem, geom, matches); err != nil {
|
|
|
|
return err, false
|
|
|
|
}
|
|
|
|
return nil, true
|
|
|
|
}
|
|
|
|
// TODO: We could also apply this shortcut for simple Polygons that we we don't
|
|
|
|
// need to make valid (e.g. no holes, only 4 points).
|
|
|
|
// However, this does not work with (Webmerc)Area columns, unless we have
|
|
|
|
// a non-GEOS implementation.
|
|
|
|
// if ww.limiter == nil && isPolygon && len(w.Nodes) <= 5 {
|
|
|
|
// geom := geomp.Geometry{Wkb: geomp.WayAsEWKBHexPolygon(w.Nodes, ww.srid)}
|
|
|
|
// if err := ww.inserter.InsertPolygon(w.OSMElem, geom, matches); err != nil {
|
|
|
|
// return err, false
|
|
|
|
// }
|
|
|
|
// return nil, true
|
|
|
|
// }
|
|
|
|
|
|
|
|
var err error
|
|
|
|
var geosgeom *geos.Geom
|
|
|
|
|
2013-10-28 14:28:56 +04:00
|
|
|
if isPolygon {
|
2015-04-30 10:42:49 +03:00
|
|
|
geosgeom, err = geomp.Polygon(g, way.Nodes)
|
2015-11-21 20:10:20 +03:00
|
|
|
if err == nil {
|
2017-11-21 11:18:44 +03:00
|
|
|
if g.NumCoordinates(geosgeom) > 5 {
|
|
|
|
// only check for valididty for non-simple geometries
|
|
|
|
geosgeom, err = g.MakeValid(geosgeom)
|
|
|
|
}
|
2015-11-21 20:10:20 +03:00
|
|
|
}
|
2013-10-28 14:28:56 +04:00
|
|
|
} else {
|
2015-04-30 10:42:49 +03:00
|
|
|
geosgeom, err = geomp.LineString(g, way.Nodes)
|
2013-10-28 14:28:56 +04:00
|
|
|
}
|
2013-05-28 14:54:19 +04:00
|
|
|
if err != nil {
|
2017-02-01 13:59:41 +03:00
|
|
|
return err, false
|
2013-05-28 14:54:19 +04:00
|
|
|
}
|
2013-06-19 11:19:21 +04:00
|
|
|
|
2015-04-30 10:42:49 +03:00
|
|
|
geom, err := geomp.AsGeomElement(g, geosgeom)
|
2013-06-19 11:19:21 +04:00
|
|
|
if err != nil {
|
2017-02-01 13:59:41 +03:00
|
|
|
return err, false
|
2013-06-19 11:19:21 +04:00
|
|
|
}
|
|
|
|
|
2017-02-01 13:59:41 +03:00
|
|
|
inserted := true
|
2013-07-30 10:17:47 +04:00
|
|
|
if ww.limiter != nil {
|
2015-04-30 10:42:49 +03:00
|
|
|
parts, err := ww.limiter.Clip(geom.Geom)
|
2013-05-28 14:54:19 +04:00
|
|
|
if err != nil {
|
2017-02-01 13:59:41 +03:00
|
|
|
return err, false
|
|
|
|
}
|
|
|
|
if len(parts) == 0 {
|
|
|
|
// outside of limitto
|
|
|
|
inserted = false
|
2013-05-28 14:54:19 +04:00
|
|
|
}
|
2013-10-28 14:28:56 +04:00
|
|
|
for _, p := range parts {
|
2013-05-28 14:54:19 +04:00
|
|
|
way := element.Way(*w)
|
2015-04-30 10:42:49 +03:00
|
|
|
geom = geomp.Geometry{Geom: p, Wkb: g.AsEwkbHex(p)}
|
2013-10-28 14:28:56 +04:00
|
|
|
if isPolygon {
|
2015-04-30 10:42:49 +03:00
|
|
|
if err := ww.inserter.InsertPolygon(way.OSMElem, geom, matches); err != nil {
|
2017-02-01 13:59:41 +03:00
|
|
|
return err, false
|
2014-04-28 13:43:25 +04:00
|
|
|
}
|
2013-10-28 14:28:56 +04:00
|
|
|
} else {
|
2015-04-30 10:42:49 +03:00
|
|
|
if err := ww.inserter.InsertLineString(way.OSMElem, geom, matches); err != nil {
|
2017-02-01 13:59:41 +03:00
|
|
|
return err, false
|
2014-04-28 13:43:25 +04:00
|
|
|
}
|
2013-10-28 14:28:56 +04:00
|
|
|
}
|
2013-05-28 14:54:19 +04:00
|
|
|
}
|
|
|
|
} else {
|
2013-10-28 14:28:56 +04:00
|
|
|
if isPolygon {
|
2015-04-30 10:42:49 +03:00
|
|
|
if err := ww.inserter.InsertPolygon(way.OSMElem, geom, matches); err != nil {
|
2017-02-01 13:59:41 +03:00
|
|
|
return err, false
|
2014-04-28 13:43:25 +04:00
|
|
|
}
|
2013-10-28 14:28:56 +04:00
|
|
|
} else {
|
2015-04-30 10:42:49 +03:00
|
|
|
if err := ww.inserter.InsertLineString(way.OSMElem, geom, matches); err != nil {
|
2017-02-01 13:59:41 +03:00
|
|
|
return err, false
|
2014-04-28 13:43:25 +04:00
|
|
|
}
|
2013-10-28 14:28:56 +04:00
|
|
|
}
|
2013-05-28 14:54:19 +04:00
|
|
|
}
|
2017-02-01 13:59:41 +03:00
|
|
|
return nil, inserted
|
2013-05-28 14:54:19 +04:00
|
|
|
}
|