2013-05-21 11:50:10 +04:00
|
|
|
package writer
|
|
|
|
|
|
|
|
import (
|
2014-08-11 12:18:08 +04:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
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"
|
2016-01-04 13:19:28 +03:00
|
|
|
geosp "github.com/omniscale/imposm3/geom/geos"
|
2014-08-04 17:19:35 +04:00
|
|
|
"github.com/omniscale/imposm3/mapping"
|
|
|
|
"github.com/omniscale/imposm3/stats"
|
2013-05-21 11:50:10 +04:00
|
|
|
)
|
|
|
|
|
|
|
|
type RelationWriter struct {
|
2013-05-28 14:54:19 +04:00
|
|
|
OsmElemWriter
|
2016-01-04 13:19:28 +03:00
|
|
|
singleIdSpace bool
|
|
|
|
rel chan *element.Relation
|
|
|
|
polygonMatcher mapping.RelWayMatcher
|
|
|
|
relationMatcher mapping.RelationMatcher
|
|
|
|
relationMemberMatcher mapping.RelationMatcher
|
|
|
|
maxGap float64
|
2013-05-21 11:50:10 +04:00
|
|
|
}
|
|
|
|
|
2014-05-05 12:21:46 +04:00
|
|
|
func NewRelationWriter(
|
|
|
|
osmCache *cache.OSMCache,
|
|
|
|
diffCache *cache.DiffCache,
|
2014-08-11 12:18:08 +04:00
|
|
|
singleIdSpace bool,
|
2014-05-05 12:21:46 +04:00
|
|
|
rel chan *element.Relation,
|
|
|
|
inserter database.Inserter,
|
|
|
|
progress *stats.Statistics,
|
2014-06-19 13:51:15 +04:00
|
|
|
matcher mapping.RelWayMatcher,
|
2016-01-04 13:19:28 +03:00
|
|
|
relMatcher mapping.RelationMatcher,
|
|
|
|
relMemberMatcher mapping.RelationMatcher,
|
2014-05-05 12:21:46 +04:00
|
|
|
srid int,
|
|
|
|
) *OsmElemWriter {
|
2014-10-22 15:36:06 +04:00
|
|
|
maxGap := 1e-1 // 0.1m
|
2014-10-21 19:08:56 +04:00
|
|
|
if srid == 4326 {
|
2014-10-22 15:36:06 +04:00
|
|
|
maxGap = 1e-6 // ~0.1m
|
2014-10-21 19:08:56 +04:00
|
|
|
}
|
2013-05-21 11:50:10 +04:00
|
|
|
rw := RelationWriter{
|
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
|
|
|
},
|
2016-01-04 13:19:28 +03:00
|
|
|
singleIdSpace: singleIdSpace,
|
|
|
|
polygonMatcher: matcher,
|
|
|
|
relationMatcher: relMatcher,
|
|
|
|
relationMemberMatcher: relMemberMatcher,
|
|
|
|
rel: rel,
|
|
|
|
maxGap: maxGap,
|
2013-05-21 11:50:10 +04:00
|
|
|
}
|
2013-05-28 14:54:19 +04:00
|
|
|
rw.OsmElemWriter.writer = &rw
|
|
|
|
return &rw.OsmElemWriter
|
2013-05-21 11:50:10 +04:00
|
|
|
}
|
|
|
|
|
2014-08-11 12:18:08 +04:00
|
|
|
func (rw *RelationWriter) relId(id int64) int64 {
|
|
|
|
if !rw.singleIdSpace {
|
|
|
|
return -id
|
|
|
|
}
|
|
|
|
return element.RelIdOffset - id
|
|
|
|
}
|
|
|
|
|
2013-05-21 11:50:10 +04:00
|
|
|
func (rw *RelationWriter) loop() {
|
2016-01-04 13:19:28 +03:00
|
|
|
geos := geosp.NewGeos()
|
2013-06-19 11:19:21 +04:00
|
|
|
geos.SetHandleSrid(rw.srid)
|
2013-05-28 10:59:59 +04:00
|
|
|
defer geos.Finish()
|
|
|
|
|
2013-06-07 10:25:45 +04:00
|
|
|
NextRel:
|
2013-05-21 11:50:10 +04:00
|
|
|
for r := range rw.rel {
|
|
|
|
rw.progress.AddRelations(1)
|
|
|
|
err := rw.osmCache.Ways.FillMembers(r.Members)
|
2013-06-07 10:25:45 +04:00
|
|
|
if err != nil {
|
|
|
|
if err != cache.NotFound {
|
2013-11-07 18:48:55 +04:00
|
|
|
log.Warn(err)
|
2013-06-07 10:25:45 +04:00
|
|
|
}
|
2016-01-06 10:46:44 +03:00
|
|
|
continue
|
2013-05-21 11:50:10 +04:00
|
|
|
}
|
2016-01-04 13:19:28 +03:00
|
|
|
for i, m := range r.Members {
|
2013-05-21 11:50:10 +04:00
|
|
|
if m.Way == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
err := rw.osmCache.Coords.FillWay(m.Way)
|
2013-06-07 10:25:45 +04:00
|
|
|
if err != nil {
|
|
|
|
if err != cache.NotFound {
|
2013-11-07 18:48:55 +04:00
|
|
|
log.Warn(err)
|
2013-06-07 10:25:45 +04:00
|
|
|
}
|
|
|
|
continue NextRel
|
2013-05-21 11:50:10 +04:00
|
|
|
}
|
2014-06-30 10:58:22 +04:00
|
|
|
rw.NodesToSrid(m.Way.Nodes)
|
2016-01-04 13:19:28 +03:00
|
|
|
r.Members[i].Elem = &m.Way.OSMElem
|
|
|
|
}
|
|
|
|
|
2016-01-06 10:46:44 +03:00
|
|
|
// handleRelation updates r.Members but we need all of them
|
|
|
|
// for the diffCache
|
|
|
|
allMembers := r.Members
|
2016-01-04 13:19:28 +03:00
|
|
|
|
2016-01-06 10:46:44 +03:00
|
|
|
inserted := false
|
2016-01-04 13:19:28 +03:00
|
|
|
|
2016-01-06 10:46:44 +03:00
|
|
|
if handleRelationMembers(rw, r, geos) {
|
|
|
|
inserted = true
|
|
|
|
}
|
|
|
|
if handleRelation(rw, r, geos) {
|
|
|
|
inserted = true
|
|
|
|
}
|
|
|
|
if handleMultiPolygon(rw, r, geos) {
|
|
|
|
inserted = true
|
|
|
|
}
|
2016-01-04 13:19:28 +03:00
|
|
|
|
2016-01-06 10:46:44 +03:00
|
|
|
if inserted && rw.diffCache != nil {
|
|
|
|
rw.diffCache.Ways.AddFromMembers(r.Id, allMembers)
|
2016-01-08 18:08:00 +03:00
|
|
|
rw.diffCache.CoordsRel.AddFromMembers(r.Id, allMembers)
|
2016-01-06 10:46:44 +03:00
|
|
|
for _, member := range allMembers {
|
|
|
|
if member.Way != nil {
|
|
|
|
rw.diffCache.Coords.AddFromWay(member.Way)
|
2016-01-04 13:19:28 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-06 10:46:44 +03:00
|
|
|
if inserted && rw.expireor != nil {
|
|
|
|
for _, m := range allMembers {
|
|
|
|
if m.Way != nil {
|
2016-11-22 19:06:58 +03:00
|
|
|
expire.ExpireNodes(rw.expireor, m.Way.Nodes, rw.srid)
|
2016-01-06 10:46:44 +03:00
|
|
|
}
|
|
|
|
}
|
2013-05-21 11:50:10 +04:00
|
|
|
}
|
2016-01-06 10:46:44 +03:00
|
|
|
}
|
|
|
|
rw.wg.Done()
|
|
|
|
}
|
2013-05-21 11:50:10 +04:00
|
|
|
|
2016-01-06 10:46:44 +03:00
|
|
|
func handleMultiPolygon(rw *RelationWriter, r *element.Relation, geos *geosp.Geos) bool {
|
|
|
|
// prepare relation first (build rings and compute actual
|
|
|
|
// relation tags)
|
|
|
|
prepedRel, err := geomp.PrepareRelation(r, rw.srid, rw.maxGap)
|
|
|
|
if err != nil {
|
|
|
|
if errl, ok := err.(ErrorLevel); !ok || errl.Level() > 0 {
|
|
|
|
log.Warn(err)
|
2013-07-15 13:39:43 +04:00
|
|
|
}
|
2016-01-06 10:46:44 +03:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// check for matches befor building the geometry
|
|
|
|
matches := rw.polygonMatcher.MatchRelation(r)
|
|
|
|
if matches == nil {
|
|
|
|
return false
|
|
|
|
}
|
2013-07-15 13:39:43 +04:00
|
|
|
|
2016-01-06 10:46:44 +03:00
|
|
|
// build the multipolygon
|
|
|
|
geom, err := prepedRel.Build()
|
|
|
|
if geom.Geom != nil {
|
|
|
|
defer geos.Destroy(geom.Geom)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
if errl, ok := err.(ErrorLevel); !ok || errl.Level() > 0 {
|
|
|
|
log.Warn(err)
|
2013-07-15 13:39:43 +04:00
|
|
|
}
|
2016-01-06 10:46:44 +03:00
|
|
|
return false
|
|
|
|
}
|
2013-07-15 13:39:43 +04:00
|
|
|
|
2016-01-06 10:46:44 +03:00
|
|
|
if rw.limiter != nil {
|
|
|
|
start := time.Now()
|
|
|
|
parts, err := rw.limiter.Clip(geom.Geom)
|
2013-05-21 11:50:10 +04:00
|
|
|
if err != nil {
|
2016-01-06 10:46:44 +03:00
|
|
|
log.Warn(err)
|
|
|
|
return false
|
2013-05-21 11:50:10 +04:00
|
|
|
}
|
2016-01-06 10:46:44 +03:00
|
|
|
if duration := time.Now().Sub(start); duration > time.Minute {
|
|
|
|
log.Warnf("clipping relation %d to -limitto took %s", r.Id, duration)
|
|
|
|
}
|
|
|
|
for _, g := range parts {
|
2013-11-07 19:09:08 +04:00
|
|
|
rel := element.Relation(*r)
|
2014-08-11 12:18:08 +04:00
|
|
|
rel.Id = rw.relId(r.Id)
|
2016-01-06 10:46:44 +03:00
|
|
|
geom = geomp.Geometry{Geom: g, Wkb: geos.AsEwkbHex(g)}
|
2015-04-30 10:42:49 +03:00
|
|
|
err := rw.inserter.InsertPolygon(rel.OSMElem, geom, matches)
|
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-07-15 13:39:43 +04:00
|
|
|
}
|
2016-01-06 10:46:44 +03:00
|
|
|
} else {
|
|
|
|
rel := element.Relation(*r)
|
|
|
|
rel.Id = rw.relId(r.Id)
|
|
|
|
err := rw.inserter.InsertPolygon(rel.OSMElem, geom, matches)
|
|
|
|
if err != nil {
|
|
|
|
if errl, ok := err.(ErrorLevel); !ok || errl.Level() > 0 {
|
2013-12-16 19:54:42 +04:00
|
|
|
log.Warn(err)
|
2013-12-12 20:12:05 +04:00
|
|
|
}
|
2016-01-06 10:46:44 +03:00
|
|
|
return false
|
2013-07-15 13:39:43 +04:00
|
|
|
}
|
2016-01-06 10:46:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, m := range mapping.SelectRelationPolygons(rw.polygonMatcher, r) {
|
|
|
|
err = rw.osmCache.InsertedWays.PutWay(m.Way)
|
|
|
|
if err != nil {
|
|
|
|
log.Warn(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleRelation(rw *RelationWriter, r *element.Relation, geos *geosp.Geos) bool {
|
|
|
|
relMatches := rw.relationMatcher.MatchRelation(r)
|
|
|
|
if relMatches == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
rel := element.Relation(*r)
|
|
|
|
rel.Id = rw.relId(r.Id)
|
|
|
|
rw.inserter.InsertPolygon(rel.OSMElem, geomp.Geometry{}, relMatches)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleRelationMembers(rw *RelationWriter, r *element.Relation, geos *geosp.Geos) bool {
|
|
|
|
relMemberMatches := rw.relationMemberMatcher.MatchRelation(r)
|
|
|
|
if relMemberMatches == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for i, m := range r.Members {
|
|
|
|
if m.Type == element.RELATION {
|
|
|
|
mrel, err := rw.osmCache.Relations.GetRelation(m.Id)
|
|
|
|
if err != nil {
|
2016-04-27 10:45:28 +03:00
|
|
|
if err != cache.NotFound {
|
2016-01-06 10:46:44 +03:00
|
|
|
log.Warn(err)
|
2013-05-30 18:04:14 +04:00
|
|
|
}
|
2016-04-27 10:45:28 +03:00
|
|
|
return false
|
2013-05-30 18:04:14 +04:00
|
|
|
}
|
2016-01-06 10:46:44 +03:00
|
|
|
r.Members[i].Elem = &mrel.OSMElem
|
|
|
|
} else if m.Type == element.NODE {
|
|
|
|
nd, err := rw.osmCache.Nodes.GetNode(m.Id)
|
|
|
|
if err != nil {
|
|
|
|
if err == cache.NotFound {
|
|
|
|
nd, err = rw.osmCache.Coords.GetCoord(m.Id)
|
|
|
|
if err != nil {
|
|
|
|
if err != cache.NotFound {
|
|
|
|
log.Warn(err)
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log.Warn(err)
|
|
|
|
return false
|
2013-07-15 13:14:03 +04:00
|
|
|
}
|
|
|
|
}
|
2016-01-06 10:46:44 +03:00
|
|
|
rw.NodeToSrid(nd)
|
|
|
|
r.Members[i].Node = nd
|
|
|
|
r.Members[i].Elem = &nd.OSMElem
|
2013-05-21 11:50:10 +04:00
|
|
|
}
|
|
|
|
}
|
2016-01-06 10:46:44 +03:00
|
|
|
|
|
|
|
for _, m := range r.Members {
|
|
|
|
var g *geosp.Geom
|
|
|
|
var err error
|
|
|
|
if m.Node != nil {
|
|
|
|
g, err = geomp.Point(geos, *m.Node)
|
|
|
|
} else if m.Way != nil {
|
|
|
|
g, err = geomp.LineString(geos, m.Way.Nodes)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Warn(err)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
var gelem geomp.Geometry
|
|
|
|
if g == nil {
|
|
|
|
g = geos.FromWkt("POLYGON EMPTY")
|
|
|
|
gelem = geomp.Geometry{Geom: g, Wkb: geos.AsEwkbHex(g)}
|
|
|
|
} else {
|
|
|
|
gelem, err = geomp.AsGeomElement(geos, g)
|
|
|
|
if err != nil {
|
|
|
|
log.Warn(err)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rel := element.Relation(*r)
|
|
|
|
rel.Id = rw.relId(r.Id)
|
|
|
|
rw.inserter.InsertRelationMember(rel, m, gelem, relMemberMatches)
|
|
|
|
}
|
|
|
|
return true
|
2013-05-21 11:50:10 +04:00
|
|
|
}
|