func TestSelectRelationPolygonsMultiple(t *testing.T) { mapping, err := NewMapping("test_mapping.json") if err != nil { t.Fatal(err) } r := element.Relation{} r.Tags = element.Tags{"landuse": "park"} r.Members = []element.Member{ makeMember(0, element.Tags{"landuse": "park"}), makeMember(1, element.Tags{"natural": "forest"}), makeMember(2, element.Tags{"landuse": "park"}), makeMember(3, element.Tags{"highway": "pedestrian"}), makeMember(4, element.Tags{"landuse": "park", "layer": "2", "name": "foo"}), } filtered := SelectRelationPolygons( mapping.PolygonMatcher(), &r, ) if len(filtered) != 3 { t.Fatal(filtered) } if filtered[0].Id != 0 || filtered[1].Id != 2 || filtered[2].Id != 4 { t.Fatal(filtered) } }
func TestMatcherMappingOrder(t *testing.T) { elem := element.Relation{} polys := mapping.PolygonMatcher() /* landusages mapping has the following order, check that XxxMatcher always uses the first amenity: - university landuse: - forest leisure: - park landuse: - park */ elem.Tags = element.Tags{"landuse": "forest", "leisure": "park"} matchesEqual(t, []Match{{"landuse", "forest", DestTable{Name: "landusages"}, nil}}, polys.MatchRelation(&elem)) elem.Tags = element.Tags{"landuse": "park", "leisure": "park"} matchesEqual(t, []Match{{"leisure", "park", DestTable{Name: "landusages"}, nil}}, polys.MatchRelation(&elem)) elem.Tags = element.Tags{"landuse": "park", "leisure": "park", "amenity": "university"} matchesEqual(t, []Match{{"amenity", "university", DestTable{Name: "landusages"}, nil}}, polys.MatchRelation(&elem)) }
func TestInsertMultipleTags(t *testing.T) { w1 := makeWay(1, element.Tags{"landusage": "forest", "highway": "secondary"}, []coord{ {1, 0, 0}, {2, 10, 0}, {3, 10, 10}, }) w2 := makeWay(2, element.Tags{"highway": "secondary"}, []coord{ {3, 10, 10}, {4, 0, 10}, {1, 0, 0}, }) rel := element.Relation{OSMElem: element.OSMElem{Id: 1, Tags: element.Tags{"landusage": "forest"}}} rel.Members = []element.Member{ {1, element.WAY, "outer", &w1}, // also highway=secondary {2, element.WAY, "inner", &w2}, } BuildRelation(&rel, 3857) g := geos.NewGeos() defer g.Finish() if rel.Tags["landusage"] != "forest" { t.Fatal("wrong rel tags", rel.Tags) } if !g.IsValid(rel.Geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) } if area := rel.Geom.Geom.Area(); area != 100 { t.Fatal("area invalid", area) } }
func TestMultiPolygonWithMultipleHoles(t *testing.T) { w1 := makeWay(1, element.Tags{"landusage": "forest"}, []coord{ {1, 0, 0}, {2, 10, 0}, {3, 10, 10}, {4, 0, 10}, {1, 0, 0}, }) w2 := makeWay(1, element.Tags{"water": "basin"}, []coord{ {1, 1, 1}, {2, 2, 1}, {3, 2, 2}, {4, 1, 2}, {1, 1, 1}, }) w3 := makeWay(3, element.Tags{"landusage": "scrub"}, []coord{ {1, 3, 3}, {2, 4, 3}, {3, 4, 4}, {4, 3, 4}, {1, 3, 3}, }) rel := element.Relation{ OSMElem: element.OSMElem{Id: 1, Tags: element.Tags{"landusage": "forest"}}} rel.Members = []element.Member{ {1, element.WAY, "outer", &w1}, {2, element.WAY, "inner", &w2}, {3, element.WAY, "inner", &w3}, } geom, err := buildRelation(&rel, 3857) if err != nil { t.Fatal(err) } g := geos.NewGeos() defer g.Finish() // if len(rel.Tags) != 1 { // t.Fatal("wrong rel tags", rel.Tags) // } // if rel.Tags["landusage"] != "forest" { // t.Fatal("wrong rel tags", rel.Tags) // } if !g.IsValid(geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } if area := geom.Geom.Area(); area != 100-1-1 { t.Fatal("area invalid", area) } }
func TestTouchingPolygonsWithHole(t *testing.T) { w1 := makeWay(1, element.Tags{"water": "riverbank"}, []coord{ {1, 0, 0}, {2, 10, 0}, {3, 10, 10}, {4, 0, 10}, {1, 0, 0}, }) w2 := makeWay(2, element.Tags{"water": "riverbank"}, []coord{ {2, 10, 0}, {5, 30, 0}, {6, 30, 10}, {3, 10, 10}, {2, 10, 0}, }) w3 := makeWay(3, element.Tags{"landusage": "forest"}, []coord{ {7, 2, 2}, {8, 8, 2}, {9, 8, 8}, {10, 2, 8}, {7, 2, 2}, }) rel := element.Relation{OSMElem: element.OSMElem{Id: 1, Tags: element.Tags{"water": "riverbank"}}} rel.Members = []element.Member{ {1, element.WAY, "outer", &w1}, {2, element.WAY, "outer", &w2}, {3, element.WAY, "inner", &w3}, } geom, err := buildRelation(&rel, 3857) if err != nil { t.Fatal(err) } g := geos.NewGeos() defer g.Finish() // if len(rel.Tags) != 1 { // t.Fatal("wrong rel tags", rel.Tags) // } // if rel.Tags["water"] != "riverbank" { // t.Fatal("wrong rel tags", rel.Tags) // } if !g.IsValid(geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } if area := geom.Geom.Area(); area != 100+200-36 { t.Fatal("area invalid", area) } }
func BenchmarkTagMatch(b *testing.B) { m, err := NewMapping("matcher_test_mapping.json") if err != nil { b.Fatal(err) } matcher := m.PolygonMatcher() for i := 0; i < b.N; i++ { e := element.Relation{} e.Tags = element.Tags{"landuse": "forest", "name": "Forest", "source": "bling", "tourism": "zoo"} if m := matcher.MatchRelation(&e); len(m) != 1 { b.Fatal(m) } } }
func TestMultiPolygonWithHoleAndRelName(t *testing.T) { w1 := makeWay(1, element.Tags{"natural": "forest", "name": "Blackwood"}, []coord{ {1, 0, 0}, {2, 10, 0}, {3, 10, 10}, {4, 0, 10}, {1, 0, 0}, }) w2 := makeWay(1, element.Tags{"landusage": "scrub"}, []coord{ {5, 2, 2}, {6, 8, 2}, {7, 8, 8}, {8, 2, 8}, {5, 2, 2}, }) rel := element.Relation{ OSMElem: element.OSMElem{Id: 1, Tags: element.Tags{"name": "rel"}}} rel.Members = []element.Member{ {1, element.WAY, "outer", &w1}, {2, element.WAY, "inner", &w2}, } geom, err := buildRelation(&rel, 3857) if err != nil { t.Fatal(err) } g := geos.NewGeos() defer g.Finish() // if len(rel.Tags) != 2 { // t.Fatal("wrong rel tags", rel.Tags) // } // if rel.Tags["natural"] != "forest" || rel.Tags["name"] != "Blackwood" { // t.Fatal("wrong rel tags", rel.Tags) // } if !g.IsValid(geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } if area := geom.Geom.Area(); area != 64 { t.Fatal("aread not 64", area) } }
func TestPolygonFromThreeWays(t *testing.T) { w1 := makeWay(1, element.Tags{"landusage": "forest"}, []coord{ {1, 0, 0}, {2, 10, 0}, {3, 10, 10}, }) w2 := makeWay(2, element.Tags{"landusage": "water"}, []coord{ {3, 10, 10}, {4, 0, 10}, }) w3 := makeWay(3, element.Tags{"landusage": "forest"}, []coord{ {4, 0, 10}, {1, 0, 0}, }) rel := element.Relation{OSMElem: element.OSMElem{Id: 1}} rel.Members = []element.Member{ {1, element.WAY, "outer", &w1}, {2, element.WAY, "inner", &w2}, {3, element.WAY, "inner", &w3}, } geom, err := buildRelation(&rel, 3857) if err != nil { t.Fatal(err) } g := geos.NewGeos() defer g.Finish() if len(rel.Tags) != 1 { t.Fatal("wrong rel tags", rel.Tags) } if rel.Tags["landusage"] != "forest" { t.Fatal("wrong rel tags", rel.Tags) } if !g.IsValid(geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } if area := geom.Geom.Area(); area != 100 { t.Fatal("area invalid", area) } }
// PrepareRelation is the first step in building a (multi-)polygon of a Relation. // It builds rings from all ways and returns an error if there are unclosed rings. // It also merges the Relation.Tags with the Tags of the outer way. func PrepareRelation(rel *element.Relation, srid int, maxRingGap float64) (PreparedRelation, error) { rings, err := buildRings(rel, maxRingGap) if err != nil { return PreparedRelation{}, err } rel.Tags = relationTags(rel.Tags, rings[0].ways[0].Tags) return PreparedRelation{rings, rel, srid}, nil }
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 TestOpenRing(t *testing.T) { w1 := makeWay(1, element.Tags{}, []coord{ {1, 0, 0}, {2, 10, 0}, {3, 10, 10}, {4, 0, 10}, }) rel := element.Relation{ OSMElem: element.OSMElem{Id: 1, Tags: element.Tags{}}} rel.Members = []element.Member{ {1, element.WAY, "outer", &w1}, } _, err := buildRelation(&rel, 3857) if err == nil { t.Fatal("no error from open ring") } }
func TestSimplePolygonWithHole(t *testing.T) { w1 := makeWay(1, element.Tags{}, []coord{ {1, 0, 0}, {2, 10, 0}, {3, 10, 10}, {4, 0, 10}, {1, 0, 0}, }) w2 := makeWay(2, element.Tags{}, []coord{ {5, 2, 2}, {6, 8, 2}, {7, 8, 8}, {8, 2, 8}, {5, 2, 2}, }) rel := element.Relation{ OSMElem: element.OSMElem{Id: 1, Tags: element.Tags{}}} rel.Members = []element.Member{ {1, element.WAY, "outer", &w1}, {2, element.WAY, "inner", &w2}, } geom, err := buildRelation(&rel, 3857) if err != nil { t.Fatal(err) } g := geos.NewGeos() defer g.Finish() if len(rel.Tags) != 0 { t.Fatal("wrong rel tags", rel.Tags) } if !g.IsValid(geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } if area := geom.Geom.Area(); area != 100-36 { t.Fatal("area invalid", area) } }
// PrepareRelation is the first step in building a (multi-)polygon of a Relation. // It builds rings from all ways and returns an error if there are unclosed rings. // It also merges the Relation.Tags with the Tags of the outer way. func PrepareRelation(rel *element.Relation, srid int, maxRingGap float64) (PreparedRelation, error) { rings, err := buildRings(rel, maxRingGap) if err != nil { return PreparedRelation{}, err } if rel.Tags["type"] == "multipolygon" && len(rel.Tags) == 1 { rel.Tags = relationTags(rel.Tags, rings[0].ways[0].Tags) //merge the Relation.Tags with the Tags of the outer way was removed } return PreparedRelation{rings, rel, srid}, nil }
func TestInsertedWaysDifferentTags(t *testing.T) { w1 := makeWay(1, element.Tags{"landusage": "forest"}, []coord{ {1, 0, 0}, {2, 10, 0}, {3, 10, 10}, }) w2 := makeWay(2, element.Tags{"highway": "secondary"}, []coord{ {3, 10, 10}, {4, 0, 10}, {1, 0, 0}, }) rel := element.Relation{OSMElem: element.OSMElem{Id: 1, Tags: element.Tags{"landusage": "forest"}}} rel.Members = []element.Member{ {1, element.WAY, "outer", &w1}, {2, element.WAY, "inner", &w2}, } geom, err := buildRelation(&rel, 3857) if err != nil { t.Fatal(err) } g := geos.NewGeos() defer g.Finish() // if len(rel.Tags) != 1 { // t.Fatal("wrong rel tags", rel.Tags) // } // if rel.Tags["landusage"] != "forest" { // t.Fatal("wrong rel tags", rel.Tags) // } if !g.IsValid(geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } if area := geom.Geom.Area(); area != 100 { t.Fatal("area invalid", area) } }
func TestSelectRelationPolygonsMultipleTags(t *testing.T) { mapping, err := NewMapping("test_mapping.json") if err != nil { t.Fatal(err) } r := element.Relation{} r.Tags = element.Tags{"landuse": "forest", "natural": "scrub"} r.Members = []element.Member{ makeMember(0, element.Tags{"natural": "scrub"}), makeMember(1, element.Tags{"landuse": "forest"}), } filtered := SelectRelationPolygons( mapping.PolygonMatcher(), &r, ) // TODO both should be filterd out, but we only get one, // because we match only one tag per table if len(filtered) != 1 { t.Fatal(filtered) } }
func TestSelectRelationPolygonsUnrelatedTags(t *testing.T) { mapping, err := NewMapping("test_mapping.json") if err != nil { t.Fatal(err) } r := element.Relation{} r.Tags = element.Tags{"landuse": "park"} r.Members = []element.Member{ makeMember(0, element.Tags{"landuse": "park", "layer": "2", "name": "foo"}), makeMember(1, element.Tags{"landuse": "forest"}), } filtered := SelectRelationPolygons( mapping.PolygonMatcher(), &r, ) if len(filtered) != 1 { t.Fatal(filtered) } if filtered[0].Id != 0 { t.Fatal(filtered) } }
func TestClosedAndOpenRing(t *testing.T) { w1 := makeWay(1, element.Tags{}, []coord{ {1, 0, 0}, {2, 10, 0}, {3, 10, 10}, {4, 0, 10}, {1, 0, 0}, }) w2 := makeWay(2, element.Tags{}, []coord{ {5, 0, 0}, {6, -5, -2}, }) rel := element.Relation{ OSMElem: element.OSMElem{Id: 1, Tags: element.Tags{}}} rel.Members = []element.Member{ {1, element.WAY, "outer", &w1}, {2, element.WAY, "outer", &w2}, } prep, err := PrepareRelation(&rel, 3857, 0.1) if err != nil { t.Fatal(err) } // open ring is excluded if len(prep.rings) != 1 { t.Fatal("expected single ring") } geom, err := prep.Build() if err != nil { t.Fatal(err) } g := geos.NewGeos() defer g.Finish() if !g.IsValid(geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } }
func BuildRelation(rel *element.Relation, srid int) error { rings, err := BuildRings(rel) 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 } return nil }
func TestSelectRelationPolygonsSimple(t *testing.T) { mapping, err := NewMapping("test_mapping.json") if err != nil { t.Fatal(err) } r := element.Relation{} r.Tags = element.Tags{"landuse": "park"} r.Members = []element.Member{ makeMember(0, element.Tags{"landuse": "forest"}), makeMember(1, element.Tags{"landuse": "park"}), makeMember(2, element.Tags{"waterway": "riverbank"}), makeMember(4, element.Tags{"foo": "bar"}), } filtered := SelectRelationPolygons( mapping.PolygonMatcher(), &r, ) if len(filtered) != 1 { t.Fatal(filtered) } if filtered[0].Id != 1 { t.Fatal(filtered[0]) } }
func TestSelectRelationPolygonsMultipleTagsOnWay(t *testing.T) { mapping, err := NewMapping("test_mapping.json") if err != nil { t.Fatal(err) } r := element.Relation{} r.Tags = element.Tags{"waterway": "riverbank"} r.Members = []element.Member{ makeMemberRole(0, element.Tags{"waterway": "riverbank", "natural": "water"}, "outer"), makeMemberRole(1, element.Tags{"natural": "water"}, "inner"), makeMemberRole(2, element.Tags{"place": "islet"}, "inner"), } filtered := SelectRelationPolygons( mapping.PolygonMatcher(), &r, ) if len(filtered) != 1 { t.Fatal(filtered) } if filtered[0].Id != 0 { t.Fatal(filtered) } }
func TestMultiPolygonWithNeastedHoles(t *testing.T) { w1 := makeWay(1, element.Tags{"landusage": "forest"}, []coord{ {1, 0, 0}, {2, 10, 0}, {3, 10, 10}, {4, 0, 10}, {1, 0, 0}, }) w2 := makeWay(2, element.Tags{"landusage": "scrub"}, []coord{ {1, 1, 1}, {2, 9, 1}, {3, 9, 9}, {4, 1, 9}, {1, 1, 1}, }) w3 := makeWay(3, element.Tags{}, []coord{ {1, 2, 2}, {2, 8, 2}, {3, 8, 8}, {4, 2, 8}, {1, 2, 2}, }) w4 := makeWay(4, element.Tags{"landusage": "scrub"}, []coord{ {1, 3, 3}, {2, 7, 3}, {3, 7, 7}, {4, 3, 7}, {1, 3, 3}, }) w5 := makeWay(5, element.Tags{"landusage": "forest"}, []coord{ {1, 4, 4}, {2, 6, 4}, {3, 6, 6}, {4, 4, 6}, {1, 4, 4}, }) rel := element.Relation{OSMElem: element.OSMElem{Id: 1}} rel.Members = []element.Member{ {1, element.WAY, "outer", &w1}, {2, element.WAY, "inner", &w2}, {3, element.WAY, "inner", &w3}, {4, element.WAY, "inner", &w4}, {5, element.WAY, "inner", &w5}, } geom, err := buildRelation(&rel, 3857) if err != nil { t.Fatal(err) } g := geos.NewGeos() defer g.Finish() // if len(rel.Tags) != 1 { // t.Fatal("wrong rel tags", rel.Tags) // } // if rel.Tags["landusage"] != "forest" { // t.Fatal("wrong rel tags", rel.Tags) // } if !g.IsValid(geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } if area := geom.Geom.Area(); area != 100-64+36-16+4 { t.Fatal("area invalid", area) } }
// 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() totalRings := len(rings) shells := map[*Ring]bool{rings[0]: true} for i := 0; i < totalRings; i++ { testGeom := g.Prepare(rings[i].geom) if testGeom == nil { return nil, errors.New("Error while preparing geometry") } for j := i + 1; j < totalRings; j++ { if g.PreparedContains(testGeom, rings[j].geom) { if rings[j].containedBy != -1 { // j is inside a larger ring, remove that relationship // e.g. j is hole inside a hole (i) delete(rings[rings[j].containedBy].holes, rings[j]) delete(shells, rings[j]) } // remember parent rings[j].containedBy = i // add ring as hole or shell if ringIsHole(rings, j) { rings[i].holes[rings[j]] = true } else { shells[rings[j]] = true } } } if rings[i].containedBy == -1 { // add as shell if it is not a hole shells[rings[i]] = true } g.PreparedDestroy(testGeom) } var polygons []*geos.Geom for shell, _ := range shells { var interiors []*geos.Geom for hole, _ := range shell.holes { hole.MarkInserted(rel.Tags) ring := g.Clone(g.ExteriorRing(hole.geom)) g.Destroy(hole.geom) if ring == nil { return nil, errors.New("Error while getting exterior ring.") } interiors = append(interiors, ring) } shell.MarkInserted(rel.Tags) exterior := g.Clone(g.ExteriorRing(shell.geom)) g.Destroy(shell.geom) if exterior == nil { return nil, errors.New("Error while getting exterior ring.") } polygon := g.Polygon(exterior, interiors) if polygon == nil { return nil, errors.New("Error while building polygon.") } polygons = append(polygons, polygon) } var result *geos.Geom if len(polygons) == 1 { result = polygons[0] } else { result = g.MultiPolygon(polygons) if result == nil { return nil, errors.New("Error while building multi-polygon.") } } if !g.IsValid(result) { buffered := g.Buffer(result, 0) if buffered == nil { return nil, errors.New("Error while fixing geom with buffer(0)") } g.Destroy(result) result = buffered } g.DestroyLater(result) insertedWays := make(map[int64]bool) for _, r := range rings { for id, _ := range r.inserted { insertedWays[id] = true } } wkb := g.AsEwkbHex(result) if wkb == nil { return nil, errors.New("unable to create WKB for relation") } rel.Geom = &element.Geometry{Geom: result, Wkb: wkb} return result, nil }
func TestBrokenPolygonSelfIntersect(t *testing.T) { // 2##3 6##7 // # # #### // 1##4____5##8 w1 := makeWay(1, element.Tags{}, []coord{ {1, 0, 0}, {2, 0, 10}, {3, 10, 10}, {4, 10, 0}, {5, 20, 0}, {6, 20, 10}, {7, 30, 10}, {8, 30, 0}, {1, 0, 0}, }) w2 := makeWay(2, element.Tags{}, []coord{ {15, 2, 2}, {16, 8, 2}, {17, 8, 8}, {18, 2, 8}, {15, 2, 2}, }) rel1 := element.Relation{OSMElem: element.OSMElem{Id: 1}} rel1.Members = []element.Member{ {1, element.WAY, "outer", &w1}, {2, element.WAY, "inner", &w2}, } geom1, err := buildRelation(&rel1, 3857) if err != nil { t.Fatal(err) } g := geos.NewGeos() defer g.Finish() // if len(rel1.Tags) != 0 { // t.Fatal("wrong rel tags", rel1.Tags) // } if !g.IsValid(geom1.Geom) { t.Fatal("geometry not valid", g.AsWkt(geom1.Geom)) } if area := geom1.Geom.Area(); area != 200-36 { t.Fatal("area invalid", area) } // 2##3 6##7 // # # #### // 1##4____5##8 w3 := makeWay(1, element.Tags{}, []coord{ {4, 10, 0}, {1, 0, 0}, {2, 0, 10}, {3, 10, 10}, {4, 10, 0}, {5, 20, 0}, {6, 20, 10}, {7, 30, 10}, {8, 30, 0}, {4, 10, 0}, }) rel2 := element.Relation{OSMElem: element.OSMElem{Id: 1}} rel2.Members = []element.Member{ {1, element.WAY, "outer", &w3}, {2, element.WAY, "inner", &w2}, } geom2, err := buildRelation(&rel2, 3857) if err != nil { t.Fatal(err) } g = geos.NewGeos() defer g.Finish() // if len(rel2.Tags) != 0 { // t.Fatal("wrong rel tags", rel2.Tags) // } if !g.IsValid(geom2.Geom) { t.Fatal("geometry not valid", g.AsWkt(geom2.Geom)) } if area := geom2.Geom.Area(); area != 200-36 { t.Fatal("area invalid", area) } }
func TestBrokenPolygonSelfIntersectTriangle(t *testing.T) { // 2### // # ###4 // # ###3 // 1### // triangle with four points, minor overlapping w1 := makeWay(1, element.Tags{}, []coord{ {1, 0, 0}, {2, 0, 100}, {3, 100, 50 - 0.00001}, {4, 100, 50 + 0.00001}, {1, 0, 0}, }) w2 := makeWay(2, element.Tags{}, []coord{ {15, 10, 45}, {16, 10, 55}, {17, 20, 55}, {18, 20, 45}, {15, 10, 45}, }) rel := element.Relation{OSMElem: element.OSMElem{Id: 1}} rel.Members = []element.Member{ {1, element.WAY, "outer", &w1}, {2, element.WAY, "inner", &w2}, } geom, err := buildRelation(&rel, 3857) if err != nil { t.Fatal(err) } g := geos.NewGeos() defer g.Finish() if !g.IsValid(geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } area := geom.Geom.Area() // as for python assertAlmostEqual(a, b) round(a-b, 7) == 0 if math.Abs(area-(100*100/2-100)) > 0.01 { t.Fatal("area invalid", area) } // larger overlap w3 := makeWay(1, element.Tags{}, []coord{ {1, 0, 0}, {2, 0, 100}, {3, 100, 50 - 1}, {4, 100, 50 + 1}, {1, 0, 0}, }) w4 := makeWay(2, element.Tags{}, []coord{ {15, 10, 45}, {16, 10, 55}, {17, 20, 55}, {18, 20, 45}, {15, 10, 45}, }) rel = element.Relation{OSMElem: element.OSMElem{Id: 1}} rel.Members = []element.Member{ {1, element.WAY, "outer", &w3}, {2, element.WAY, "inner", &w4}, } geom, err = buildRelation(&rel, 3857) if err != nil { t.Fatal(err) } if !g.IsValid(geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } area = geom.Geom.Area() if math.Abs((area - (100*98/2 - 100))) > 10 { t.Fatal("area invalid", area) } }
func TestPolygonMatcher(t *testing.T) { elem := element.Relation{} polys := mapping.PolygonMatcher() elem.Tags = element.Tags{"unknown": "baz"} matchesEqual(t, []Match{}, polys.MatchRelation(&elem)) elem.Tags = element.Tags{"landuse": "unknowns"} matchesEqual(t, []Match{}, polys.MatchRelation(&elem)) elem.Tags = element.Tags{"building": "yes"} matchesEqual(t, []Match{{"building", "yes", DestTable{"buildings", ""}, nil}}, polys.MatchRelation(&elem)) elem.Tags = element.Tags{"building": "residential"} matchesEqual(t, []Match{{"building", "residential", DestTable{"buildings", ""}, nil}}, polys.MatchRelation(&elem)) elem.Tags = element.Tags{"building": "shop"} matchesEqual(t, []Match{ {"building", "shop", DestTable{"buildings", ""}, nil}, {"building", "shop", DestTable{"amenity_areas", ""}, nil}}, polys.MatchRelation(&elem)) elem.Tags = element.Tags{"landuse": "farm"} matchesEqual(t, []Match{{"landuse", "farm", DestTable{"landusages", ""}, nil}}, polys.MatchRelation(&elem)) elem.Tags = element.Tags{"landuse": "farm", "highway": "secondary"} matchesEqual(t, []Match{{"landuse", "farm", DestTable{"landusages", ""}, nil}}, polys.MatchRelation(&elem)) elem.Tags = element.Tags{"landuse": "farm", "aeroway": "apron"} matchesEqual(t, []Match{ {"aeroway", "apron", DestTable{"transport_areas", ""}, nil}, {"landuse", "farm", DestTable{"landusages", ""}, nil}}, polys.MatchRelation(&elem)) elem.Tags = element.Tags{"highway": "footway"} matchesEqual(t, []Match{{"highway", "footway", DestTable{"landusages", ""}, nil}}, polys.MatchRelation(&elem)) elem.Tags = element.Tags{"boundary": "administrative", "admin_level": "8"} matchesEqual(t, []Match{{"boundary", "administrative", DestTable{"admin", ""}, nil}}, polys.MatchRelation(&elem)) }