From 198f2d925f14e319c026f506ccb2a8179d8fcdc5 Mon Sep 17 00:00:00 2001 From: Oliver Tonnhofer Date: Mon, 15 Jul 2013 11:39:43 +0200 Subject: [PATCH] filter by relation tags before finishing the multipolygon --- geom/index/spatialindex.go | 26 +++++++++++++ geom/multipolygon.go | 51 +++++++++++++++++++------ writer/relations.go | 76 +++++++++++++++++++++++--------------- 3 files changed, 113 insertions(+), 40 deletions(-) create mode 100644 geom/index/spatialindex.go diff --git a/geom/index/spatialindex.go b/geom/index/spatialindex.go new file mode 100644 index 0000000..bca5b83 --- /dev/null +++ b/geom/index/spatialindex.go @@ -0,0 +1,26 @@ +package main + +import ( + "flag" + "fmt" + "goposm/geom/clipper" + "goposm/geom/geos" + "log" +) + +func main() { + flag.Parse() + + clipper, err := clipper.NewFromOgrSource(flag.Arg(0)) + if err != nil { + log.Fatal(err) + } + + g := geos.NewGeos() + defer g.Finish() + + line := g.FromWkt("LINESTRING(1106543 7082055, 1107105.2 7087540.0)") + + result, err := clipper.Clip(line) + fmt.Println(result, err) +} diff --git a/geom/multipolygon.go b/geom/multipolygon.go index 8bbf0db..e0397de 100644 --- a/geom/multipolygon.go +++ b/geom/multipolygon.go @@ -12,6 +12,9 @@ func BuildRelation(rel *element.Relation, srid int) error { if err != nil { return err } + + rel.Tags = relationTags(rel.Tags, rings[0].ways[0].Tags) + _, err = BuildRelGeometry(rel, rings, srid) if err != nil { return err @@ -19,6 +22,32 @@ func BuildRelation(rel *element.Relation, srid int) error { return nil } +type preparedRelation struct { + rings []*Ring + rel *element.Relation + srid int +} + +func PrepareRelation(rel *element.Relation, srid int) (*preparedRelation, error) { + rings, err := BuildRings(rel) + if err != nil { + return nil, err + } + + rel.Tags = relationTags(rel.Tags, rings[0].ways[0].Tags) + + return &preparedRelation{rings, rel, srid}, nil +} + +func (prep *preparedRelation) Build() (*element.Relation, error) { + _, err := BuildRelGeometry(prep.rel, prep.rings, prep.srid) + if err != nil { + return nil, err + } + return prep.rel, nil + +} + func destroyRings(g *geos.Geos, rings []*Ring) { for _, r := range rings { if r.geom != nil { @@ -83,6 +112,13 @@ func BuildRings(rel *element.Relation) ([]*Ring, error) { } completeRings = append(completeRings, mergedRings...) + + // sort by area (large to small) + for _, r := range completeRings { + r.area = r.geom.Area() + } + sort.Sort(SortableRingsDesc(completeRings)) + return completeRings, nil } @@ -92,17 +128,13 @@ func (r SortableRingsDesc) Len() int { return len(r) } func (r SortableRingsDesc) Less(i, j int) bool { return r[i].area > r[j].area } func (r SortableRingsDesc) Swap(i, j int) { r[i], r[j] = r[j], r[i] } +// BuildRelGeometry builds the geometry of rel by creating a multipolygon of all rings. +// rings need to be sorted by area (large to small). func BuildRelGeometry(rel *element.Relation, rings []*Ring, srid int) (*geos.Geom, error) { g := geos.NewGeos() g.SetHandleSrid(srid) defer g.Finish() - // sort by area (large to small) - for _, r := range rings { - r.area = r.geom.Area() - } - sort.Sort(SortableRingsDesc(rings)) - totalRings := len(rings) shells := map[*Ring]bool{rings[0]: true} for i := 0; i < totalRings; i++ { @@ -135,13 +167,11 @@ func BuildRelGeometry(rel *element.Relation, rings []*Ring, srid int) (*geos.Geo g.PreparedDestroy(testGeom) } - relTags := relationTags(rel.Tags, rings[0].ways[0].Tags) - var polygons []*geos.Geom for shell, _ := range shells { var interiors []*geos.Geom for hole, _ := range shell.holes { - hole.MarkInserted(relTags) + hole.MarkInserted(rel.Tags) ring := g.Clone(g.ExteriorRing(hole.geom)) g.Destroy(hole.geom) if ring == nil { @@ -149,7 +179,7 @@ func BuildRelGeometry(rel *element.Relation, rings []*Ring, srid int) (*geos.Geo } interiors = append(interiors, ring) } - shell.MarkInserted(relTags) + shell.MarkInserted(rel.Tags) exterior := g.Clone(g.ExteriorRing(shell.geom)) g.Destroy(shell.geom) if exterior == nil { @@ -202,7 +232,6 @@ func BuildRelGeometry(rel *element.Relation, rings []*Ring, srid int) (*geos.Geo return nil, errors.New("unable to create WKB for relation") } rel.Geom = &element.Geometry{Geom: result, Wkb: wkb} - rel.Tags = relTags return result, nil } diff --git a/writer/relations.go b/writer/relations.go index 5e0bf6f..06a3f01 100644 --- a/writer/relations.go +++ b/writer/relations.go @@ -72,7 +72,27 @@ NextRel: // for the diffCache allMembers := r.Members - err = geom.BuildRelation(r, rw.srid) + // prepare relation first (build rings and compute actual + // relation tags) + prepedRel, err := geom.PrepareRelation(r, rw.srid) + if err != nil { + if err, ok := err.(ErrorLevel); ok { + if err.Level() <= 0 { + continue NextRel + } + } + log.Println(err) + continue NextRel + } + + // check for matches befor building the geometry + matches := rw.tagMatcher.Match(&r.Tags) + if len(matches) == 0 { + continue NextRel + } + + // build the multipolygon + r, err = prepedRel.Build() if err != nil { if r.Geom != nil && r.Geom.Geom != nil { geos.Destroy(r.Geom.Geom) @@ -87,38 +107,36 @@ NextRel: continue NextRel } - if matches := rw.tagMatcher.Match(&r.Tags); len(matches) > 0 { - if rw.clipper != nil { - parts, err := rw.clipper.Clip(r.Geom.Geom) - if err != nil { - log.Println(err) - continue NextRel - } - for _, g := range parts { - rel := element.Relation(*r) - rel.Geom = &element.Geometry{g, geos.AsEwkbHex(g)} - rw.insertMatches(&rel.OSMElem, matches) - } - } else { - rw.insertMatches(&r.OSMElem, matches) - } - err := rw.osmCache.InsertedWays.PutMembers(r.Members) + if rw.clipper != nil { + parts, err := rw.clipper.Clip(r.Geom.Geom) if err != nil { - fmt.Println(err) + log.Println(err) + continue NextRel } - if rw.diffCache != nil { - rw.diffCache.Ways.AddFromMembers(r.Id, allMembers) - for _, member := range allMembers { - if member.Way != nil { - rw.diffCache.Coords.AddFromWay(member.Way) - } + for _, g := range parts { + rel := element.Relation(*r) + rel.Geom = &element.Geometry{g, geos.AsEwkbHex(g)} + rw.insertMatches(&rel.OSMElem, matches) + } + } else { + rw.insertMatches(&r.OSMElem, matches) + } + err = rw.osmCache.InsertedWays.PutMembers(r.Members) + if err != nil { + fmt.Println(err) + } + if rw.diffCache != nil { + rw.diffCache.Ways.AddFromMembers(r.Id, allMembers) + for _, member := range allMembers { + if member.Way != nil { + rw.diffCache.Coords.AddFromWay(member.Way) } } - if rw.expireTiles != nil { - for _, m := range allMembers { - if m.Way != nil { - rw.expireTiles.ExpireFromNodes(m.Way.Nodes) - } + } + if rw.expireTiles != nil { + for _, m := range allMembers { + if m.Way != nil { + rw.expireTiles.ExpireFromNodes(m.Way.Nodes) } } }