func (nw *NodeWriter) loop() { geos := geos.NewGeos() geos.SetHandleSrid(nw.srid) defer geos.Finish() for n := range nw.nodes { nw.progress.AddNodes(1) if matches := nw.tagMatcher.Match(&n.Tags); len(matches) > 0 { proj.NodeToMerc(n) if nw.expireTiles != nil { nw.expireTiles.ExpireFromNodes([]element.Node{*n}) } point, err := geom.Point(geos, *n) if err != nil { if err, ok := err.(ErrorLevel); ok { if err.Level() <= 0 { continue } } log.Println(err) continue } n.Geom, err = geom.AsGeomElement(geos, point) if err != nil { log.Println(err) continue } if nw.limiter != nil { parts, err := nw.limiter.Clip(n.Geom.Geom) if err != nil { log.Println(err) continue } if len(parts) >= 1 { nw.insertMatches(&n.OSMElem, matches) } } else { nw.insertMatches(&n.OSMElem, matches) } } } nw.wg.Done() }
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 TestSplitPolygonAtGrids(t *testing.T) { expected := []geos.Bounds{ {0, 0, 0.05, 0.05}, {0, 0.05, 0.05, 0.1}, {0.05, 0, 0.1, 0.05}, {0.05, 0.05, 0.1, 0.1}, {0, 0.1, 0.05, 0.11}, {0.05, 0.1, 0.1, 0.11}, {0.1, 0, 0.15, 0.05}, {0.1, 0.05, 0.15, 0.1}, {0.1, 0.1, 0.15, 0.11}, } g := geos.NewGeos() defer g.Finish() geom := g.BoundsPolygon(geos.Bounds{0, 0, 0.15, 0.11}) geoms, _ := SplitPolygonAtGrid(g, geom, 0.05, 0.2) for _, geom := range geoms { t.Log(geom.Bounds()) } for i, geom := range geoms { if expected[i] != geom.Bounds() { t.Fatalf("%v != %v\n", expected[i], geom.Bounds()) } } }
func ParseGeoJson(r io.Reader) ([]*geos.Geom, error) { decoder := json.NewDecoder(r) obj := &object{} err := decoder.Decode(obj) if err != nil { return nil, err } polygons, err := constructPolygons(obj) if err != nil { return nil, err } g := geos.NewGeos() defer g.Finish() result := []*geos.Geom{} for _, p := range polygons { geom, err := geosPolygon(g, p) if err != nil { return nil, err } result = append(result, geom) } return result, err }
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}, } BuildRelation(&rel, 3857) g := geos.NewGeos() defer g.Finish() if len(rel.Members) != 1 { t.Fatal("wrong rel members", rel.Members) } if rel.Members[0].Id != 1 { t.Fatal("wrong rel members", rel.Members) } 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(rel.Geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) } if area := rel.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}, } BuildRelation(&rel, 3857) g := geos.NewGeos() defer g.Finish() if len(rel.Members) != 2 { t.Fatal("wrong rel members", rel.Members) } if rel.Members[0].Id != 1 || rel.Members[1].Id != 2 { t.Fatal("wrong rel members", rel.Members) } 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(rel.Geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) } if area := rel.Geom.Geom.Area(); area != 100+200-36 { t.Fatal("area invalid", area) } }
func (ww *WayWriter) loop() { geos := geos.NewGeos() geos.SetHandleSrid(ww.srid) defer geos.Finish() for w := range ww.ways { ww.progress.AddWays(1) if len(w.Tags) == 0 { continue } insertedAsRelation, err := ww.osmCache.InsertedWays.IsInserted(w.Id) if err != nil { log.Warn(err) continue } err = ww.osmCache.Coords.FillWay(w) if err != nil { continue } proj.NodesToMerc(w.Nodes) inserted := false if ok, matches := ww.inserter.ProbeLineString(w.OSMElem); ok { err := ww.buildAndInsert(geos, w, matches, false) if err != nil { if errl, ok := err.(ErrorLevel); !ok || errl.Level() > 0 { log.Warn(err) } continue } inserted = true } if w.IsClosed() && !insertedAsRelation { // only add polygons that were not inserted as a MultiPolygon relation if ok, matches := ww.inserter.ProbePolygon(w.OSMElem); ok { err := ww.buildAndInsert(geos, w, matches, true) if err != nil { if errl, ok := err.(ErrorLevel); !ok || errl.Level() > 0 { log.Warn(err) } continue } inserted = true } } if inserted && ww.expireor != nil { expire.ExpireNodes(ww.expireor, w.Nodes) } if ww.diffCache != nil { ww.diffCache.Coords.AddFromWay(w) } } ww.wg.Done() }
func TestPolygonNotClosed(t *testing.T) { nodes := []element.Node{ element.Node{Lat: 0, Long: 0}, element.Node{Lat: 0, Long: 10}, element.Node{Lat: 10, Long: 10}, } g := geos.NewGeos() defer g.Finish() _, err := Polygon(g, nodes) if err == nil { t.Fatal("no error") } }
func BenchmarkLineString(b *testing.B) { size := 16 nodes := make([]element.Node, size) for i := 0; i < size; i++ { nodes[i] = element.Node{Lat: 0, Long: float64(i)} } g := geos.NewGeos() defer g.Finish() for i := 0; i < b.N; i++ { LineString(g, nodes) } }
func TestClipperWithBuffer(t *testing.T) { g := geos.NewGeos() defer g.Finish() limiter, err := NewFromGeoJsonWithBuffered("./hamburg_clip.geojson", 10000.0) if err != nil { t.Fatal(err) } if limiter.IntersectsBuffer(g, 1106543, 7082055) != true { t.Fatal() } if limiter.IntersectsBuffer(g, 1006543, 7082055) != false { t.Fatal() } }
func (nw *NodeWriter) loop() { geos := geos.NewGeos() geos.SetHandleSrid(nw.srid) defer geos.Finish() for n := range nw.nodes { nw.progress.AddNodes(1) if ok, matches := nw.inserter.ProbePoint(n.OSMElem); ok { proj.NodeToMerc(n) if nw.expireor != nil { nw.expireor.Expire(n.Long, n.Lat) } point, err := geom.Point(geos, *n) if err != nil { if errl, ok := err.(ErrorLevel); !ok || errl.Level() > 0 { log.Warn(err) } continue } n.Geom, err = geom.AsGeomElement(geos, point) if err != nil { log.Warn(err) continue } if nw.limiter != nil { parts, err := nw.limiter.Clip(n.Geom.Geom) if err != nil { log.Warn(err) continue } if len(parts) >= 1 { if err := nw.inserter.InsertPoint(n.OSMElem, matches); err != nil { log.Warn(err) continue } } } else { if err := nw.inserter.InsertPoint(n.OSMElem, matches); err != nil { log.Warn(err) continue } } } } nw.wg.Done() }
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}, } BuildRelation(&rel, 3857) g := geos.NewGeos() defer g.Finish() if len(rel.Members) != 1 { t.Fatal("wrong rel members", rel.Members) } if rel.Members[0].Id != 1 { t.Fatal("wrong rel members", rel.Members) } 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(rel.Geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) } if area := rel.Geom.Geom.Area(); area != 64 { t.Fatal("aread not 64", area) } }
func TestLineString(t *testing.T) { nodes := make([]element.Node, 2) nodes[0] = element.Node{Lat: 0, Long: 0} nodes[1] = element.Node{Lat: 0, Long: 10} g := geos.NewGeos() defer g.Finish() geom, err := LineString(g, nodes) if err != nil { t.Fatal(err) } if geom.Length() != 10.0 { t.Fatal(geom.Length) } }
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}, } BuildRelation(&rel, 3857) g := geos.NewGeos() defer g.Finish() if len(rel.Members) != 2 { t.Fatal("wrong rel members", rel.Members) } if rel.Members[0].Id != 1 || rel.Members[1].Id != 3 { t.Fatal("wrong rel members", rel.Members) } 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(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 (layer *Layer) Geoms() chan *geos.Geom { geoms := make(chan *geos.Geom) go func() { defer close(geoms) g := geos.NewGeos() defer g.Finish() wkbs := layer.Wkbs() for wkb := range wkbs { geom := g.FromWkb(wkb) geoms <- geom } }() return geoms }
func TestFilterGeometryByType(t *testing.T) { g := geos.NewGeos() defer g.Finish() var result []*geos.Geom // filtered out result = filterGeometryByType(g, g.FromWkt("POINT(0 0)"), "Polygon") if len(result) != 0 { t.Fatal() } result = filterGeometryByType(g, g.FromWkt("POINT(0 0)"), "Point") if len(result) != 1 { t.Fatal() } // filtered out result = filterGeometryByType(g, g.FromWkt("LINESTRING(0 0, 10 0)"), "Polygon") if len(result) != 0 { t.Fatal() } // polygon <-> multipolygon types are compatible in both directions result = filterGeometryByType(g, g.FromWkt("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))"), "Polygon") if len(result) != 1 { t.Fatal() } result = filterGeometryByType(g, g.FromWkt("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))"), "MultiPolygon") if len(result) != 1 { t.Fatal() } result = filterGeometryByType(g, g.FromWkt("MULTIPOLYGON(((0 0, 10 0, 10 10, 0 10, 0 0)))"), "Polygon") if len(result) != 1 { t.Fatal() } result = filterGeometryByType(g, g.FromWkt("LINESTRING(0 0, 10 0)"), "LineString") if len(result) != 1 { t.Fatal() } // multilinestrings are split result = filterGeometryByType(g, g.FromWkt("MULTILINESTRING((0 0, 10 0), (20 0, 30 0))"), "LineString") if len(result) != 2 { t.Fatal() } }
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}, } BuildRelation(&rel, 3857) g := geos.NewGeos() defer g.Finish() if len(rel.Members) != 2 { t.Fatal("wrong rel members", rel.Members) } if rel.Members[0].Id != 1 || rel.Members[1].Id != 2 { t.Fatal("wrong rel members", rel.Members) } if len(rel.Tags) != 0 { 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-36 { t.Fatal("area invalid", area) } }
func (ww *WayWriter) loop() { geos := geos.NewGeos() geos.SetHandleSrid(ww.srid) defer geos.Finish() for w := range ww.ways { ww.progress.AddWays(1) if len(w.Tags) == 0 { continue } inserted, err := ww.osmCache.InsertedWays.IsInserted(w.Id) if err != nil { log.Println(err) continue } if inserted { continue } err = ww.osmCache.Coords.FillWay(w) if err != nil { continue } proj.NodesToMerc(w.Nodes) inserted = false if matches := ww.lineStringTagMatcher.Match(&w.Tags); len(matches) > 0 { ww.buildAndInsert(geos, w, matches, geom.LineString) inserted = true } if w.IsClosed() { if matches := ww.polygonTagMatcher.Match(&w.Tags); len(matches) > 0 { ww.buildAndInsert(geos, w, matches, geom.Polygon) inserted = true } } if inserted && ww.expireTiles != nil { ww.expireTiles.ExpireFromNodes(w.Nodes) } if ww.diffCache != nil { ww.diffCache.Coords.AddFromWay(w) } } ww.wg.Done() }
func TestPolygon(t *testing.T) { nodes := []element.Node{ element.Node{Lat: 0, Long: 0}, element.Node{Lat: 0, Long: 10}, element.Node{Lat: 10, Long: 10}, element.Node{Lat: 0, Long: 0}, } g := geos.NewGeos() defer g.Finish() geom, err := Polygon(g, nodes) if err != nil { t.Fatal(err) } if geom.Area() != 50.0 { t.Fatal(geom.Area()) } }
func BenchmarkClipper(b *testing.B) { g := geos.NewGeos() defer g.Finish() limiter, err := NewFromGeoJson("./hamburg_clip.geojson") if err != nil { b.Fatal(err) } geom := g.FromWkt("LINESTRING(1106543 7082055, 1107105.2 7087540.0)") for i := 0; i < b.N; i++ { result, err := limiter.Clip(geom) if err != nil { b.Fatal(err) } if len(result) != 2 { b.Fatal() } } }
func (l *Limiter) Clip(geom *geos.Geom) ([]*geos.Geom, error) { g := geos.NewGeos() defer g.Finish() // check if geom is completely contained l.geomPrepMu.Lock() if g.PreparedContains(l.geomPrep, geom) { l.geomPrepMu.Unlock() return []*geos.Geom{geom}, nil } l.geomPrepMu.Unlock() // we have intersections, query index to get intersecting parts hits := g.IndexQuery(l.index, geom) geomType := g.Type(geom) // too many intersecting parts, it probably faster to // intersect with the original geometry if len(hits) > 50 { newPart := g.Intersection(l.geom, geom) if newPart == nil { return nil, nil } newParts := filterGeometryByType(g, newPart, geomType) return mergeGeometries(g, newParts, geomType), nil } var intersections []*geos.Geom // intersect with each part... for _, hit := range hits { newPart := g.Intersection(hit.Geom, geom) if newPart == nil { continue } newParts := filterGeometryByType(g, newPart, geomType) for _, p := range newParts { intersections = append(intersections, p) } } // and merge parts back to our clipped intersection return mergeGeometries(g, intersections, geomType), nil }
func TestClipper(t *testing.T) { g := geos.NewGeos() defer g.Finish() limiter, err := NewFromGeoJson("./hamburg_clip.geojson") if err != nil { t.Fatal(err) } result, err := limiter.Clip(g.FromWkt("POINT(0 0)")) if err != nil || result != nil { t.Fatal(err) } result, err = limiter.Clip(g.FromWkt("POINT(1106543 7082055)")) if err != nil { t.Fatal(err) } if len(result) != 1 { t.Fatal() } result, err = limiter.Clip(g.FromWkt("LINESTRING(1106543 7082055, 1107105.2 7087540.0)")) if err != nil { t.Fatal(err) } if len(result) != 2 { t.Fatal() } geom := g.FromWkt("POLYGON((1106543 7082055, 1107105.2 7087540.0, 1112184.9 7084424.5, 1106543 7082055))") result, err = limiter.Clip(geom) if err != nil { t.Fatal(err) } if len(result) != 1 { t.Fatal() } if geom.Area() <= result[0].Area() { t.Fatalf("%f <= %f", geom.Area(), result[0].Area()) } }
func TestPolygonIntersection(t *testing.T) { nodes := []element.Node{ element.Node{Lat: 0, Long: 0}, element.Node{Lat: 0, Long: 10}, element.Node{Lat: 10, Long: 10}, element.Node{Lat: 10, Long: 0}, element.Node{Lat: 0, Long: 0}, } g := geos.NewGeos() defer g.Finish() geom, err := Polygon(g, nodes) if err != nil { t.Fatal(err) } result := g.Intersection(geom, g.FromWkt("LINESTRING(-10 5, 20 5)")) if !g.Equals(result, g.FromWkt("LINESTRING(0 5, 10 5)")) { t.Fatal(g.AsWkt(result)) } }
func (l *Limiter) Clip(geom *geos.Geom) ([]*geos.Geom, error) { g := geos.NewGeos() defer g.Finish() hits := g.IndexQuery(l.index, geom) if len(hits) == 0 { return nil, nil } geomType := g.Type(geom) var intersections []*geos.Geom for _, hit := range hits { hit.Lock() if g.PreparedContains(hit.Prepared, geom) { hit.Unlock() return []*geos.Geom{geom}, nil } if g.PreparedIntersects(hit.Prepared, geom) { hit.Unlock() newPart := g.Intersection(hit.Geom, geom) if newPart == nil { continue } newParts := filterGeometryByType(g, newPart, geomType) for _, p := range newParts { intersections = append(intersections, p) } } else { hit.Unlock() } } return mergeGeometries(g, intersections, geomType), nil }
func NewFromOgrSourceWithBuffered(source string, buffer float64) (*Limiter, error) { ds, err := ogr.Open(source) if err != nil { return nil, err } g := geos.NewGeos() defer g.Finish() layer, err := ds.Layer() if err != nil { return nil, err } index := g.CreateIndex() var polygons []*geos.Geom withBuffer := false if buffer != 0.0 { withBuffer = true } for geom := range layer.Geoms() { if withBuffer { simplified := g.SimplifyPreserveTopology(geom, 1000) if simplified == nil { return nil, errors.New("couldn't simplify limitto") } buffered := g.Buffer(simplified, buffer) g.Destroy(simplified) if buffered == nil { return nil, errors.New("couldn't buffer limitto") } g.DestroyLater(buffered) polygons = append(polygons, buffered) } parts, err := SplitPolygonAtAutoGrid(g, geom) if err != nil { return nil, err } for _, part := range parts { g.IndexAdd(index, part) } } var bbox geos.Bounds var prep *geos.PreparedGeom if len(polygons) > 0 { union := g.UnionPolygons(polygons) if union == nil { return nil, errors.New("unable to union limitto polygons") } simplified := g.SimplifyPreserveTopology(union, 5000) if simplified == nil { return nil, errors.New("unable to simplify limitto polygons") } g.Destroy(union) bbox = simplified.Bounds() prep = g.Prepare(simplified) // keep simplified around for prepared geometry if prep == nil { return nil, errors.New("unable to prepare limitto polygons") } } return &Limiter{index, prep, &sync.Mutex{}, bbox}, nil }
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}, } err := BuildRelation(&rel, 3857) if err != nil { t.Fatal(err) } g := geos.NewGeos() defer g.Finish() if !g.IsValid(rel.Geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) } area := rel.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}, } err = BuildRelation(&rel, 3857) if err != nil { t.Fatal(err) } if !g.IsValid(rel.Geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) } area = rel.Geom.Geom.Area() if math.Abs((area - (100*98/2 - 100))) > 10 { t.Fatal("area invalid", area) } }
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}, } err := BuildRelation(&rel1, 3857) if err != nil { t.Fatal(err) } g := geos.NewGeos() defer g.Finish() if len(rel1.Members) != 2 { t.Fatal("wrong rel members", rel1.Members) } if len(rel1.Tags) != 0 { t.Fatal("wrong rel tags", rel1.Tags) } if !g.IsValid(rel1.Geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(rel1.Geom.Geom)) } if area := rel1.Geom.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}, } err = BuildRelation(&rel2, 3857) if err != nil { t.Fatal(err) } g = geos.NewGeos() defer g.Finish() if len(rel2.Members) != 2 { t.Fatal("wrong rel members", rel2.Members) } if len(rel2.Tags) != 0 { t.Fatal("wrong rel tags", rel2.Tags) } if !g.IsValid(rel2.Geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(rel2.Geom.Geom)) } if area := rel2.Geom.Geom.Area(); area != 200-36 { t.Fatal("area invalid", area) } }
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}, } BuildRelation(&rel, 3857) g := geos.NewGeos() defer g.Finish() if len(rel.Members) != 3 { t.Fatal("wrong rel members", rel.Members) } if rel.Members[0].Id != 1 { t.Fatal("wrong rel members", rel.Members) } 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(rel.Geom.Geom) { t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) } if area := rel.Geom.Geom.Area(); area != 100-64+36-16+4 { t.Fatal("area invalid", area) } }
func TestMergePolygonGeometries(t *testing.T) { g := geos.NewGeos() defer g.Finish() // check non intersecting polygons // should return multipolygon geoms := []*geos.Geom{ g.BoundsPolygon(geos.Bounds{0, 0, 10, 10}), g.BoundsPolygon(geos.Bounds{20, 20, 30, 30}), } result := mergeGeometries(g, geoms, "Polygon") if len(result) != 1 { t.Fatal("not a single geometrie") } if g.Type(result[0]) != "MultiPolygon" { t.Fatal("not a multipolygon") } if !g.IsValid(result[0]) { t.Fatal("not valid") } // check intersecting polygons // should return single polygon geoms = []*geos.Geom{ g.BoundsPolygon(geos.Bounds{0, 0, 10, 10}), g.BoundsPolygon(geos.Bounds{5, 5, 30, 30}), } result = mergeGeometries(g, geoms, "Polygon") if len(result) != 1 { t.Fatal("not a single geometrie") } if g.Type(result[0]) != "Polygon" { t.Fatal("not a polygon") } if !g.IsValid(result[0]) { t.Fatal("not valid") } // same with multipolygon type geoms = []*geos.Geom{ g.BoundsPolygon(geos.Bounds{0, 0, 10, 10}), g.BoundsPolygon(geos.Bounds{5, 5, 30, 30}), } result = mergeGeometries(g, geoms, "MultiPolygon") if len(result) != 1 { t.Fatal("not a single geometrie") } if g.Type(result[0]) != "Polygon" { t.Fatal("not a polygon") } if !g.IsValid(result[0]) { t.Fatal("not valid") } // strip non Polygons geoms = []*geos.Geom{ g.FromWkt("POINT(0 0)"), g.BoundsPolygon(geos.Bounds{0, 0, 10, 10}), g.FromWkt("LINESTRING(0 0, 0 10)"), g.BoundsPolygon(geos.Bounds{5, 5, 30, 30}), } result = mergeGeometries(g, geoms, "Polygon") if len(result) != 1 { t.Fatal("not a single geometrie") } if g.Type(result[0]) != "Polygon" { t.Fatal("not a polygon") } if !g.IsValid(result[0]) { t.Fatal("not valid") } }
func ReadPbf(cache *osmcache.OSMCache, progress *stats.Statistics, tagmapping *mapping.Mapping, pbfFile *pbf.Pbf, limiter *limit.Limiter, ) { nodes := make(chan []element.Node, 4) coords := make(chan []element.Node, 4) ways := make(chan []element.Way, 4) relations := make(chan []element.Relation, 4) withLimiter := false if limiter != nil { withLimiter = true } if pbfFile.Header.Time.Unix() != 0 { log.Printf("reading %s with data till %v", pbfFile.Filename, pbfFile.Header.Time.Local()) } parser := pbf.NewParser(pbfFile, coords, nodes, ways, relations) coordsSynced := make(chan bool) coordsSync := util.NewSyncPoint(int(nCoords+nNodes), func() { coordsSynced <- true }) parser.NotifyWays(func() { for i := 0; int64(i) < nCoords; i++ { coords <- nil } for i := 0; int64(i) < nNodes; i++ { nodes <- nil } <-coordsSynced }) waysSynced := make(chan bool) waysSync := util.NewSyncPoint(int(nWays), func() { waysSynced <- true }) parser.NotifyRelations(func() { for i := 0; int64(i) < nWays; i++ { ways <- nil } <-waysSynced }) parser.Start() waitWriter := sync.WaitGroup{} for i := 0; int64(i) < nWays; i++ { waitWriter.Add(1) go func() { var skip, hit int m := tagmapping.WayTagFilter() for ws := range ways { if ws == nil { waysSync.Sync() continue } if skipWays { continue } for i, _ := range ws { m.Filter(&ws[i].Tags) if withLimiter { if !cache.Coords.FirstRefIsCached(ws[i].Refs) { ws[i].Id = osmcache.SKIP skip += 1 } else { hit += 1 } } } cache.Ways.PutWays(ws) progress.AddWays(len(ws)) } waitWriter.Done() }() } for i := 0; int64(i) < nRels; i++ { waitWriter.Add(1) go func() { var skip, hit int m := tagmapping.RelationTagFilter() for rels := range relations { numWithTags := 0 for i, _ := range rels { m.Filter(&rels[i].Tags) if len(rels[i].Tags) > 0 { numWithTags += 1 } if withLimiter { if !cache.Ways.FirstMemberIsCached(rels[i].Members) { skip += 1 rels[i].Id = osmcache.SKIP } else { hit += 1 } } } cache.Relations.PutRelations(rels) progress.AddRelations(numWithTags) } waitWriter.Done() }() } for i := 0; int64(i) < nCoords; i++ { waitWriter.Add(1) go func() { var skip, hit int g := geos.NewGeos() defer g.Finish() for nds := range coords { if nds == nil { coordsSync.Sync() continue } if withLimiter { for i, _ := range nds { nd := element.Node{Long: nds[i].Long, Lat: nds[i].Lat} proj.NodeToMerc(&nd) if !limiter.IntersectsBuffer(g, nd.Long, nd.Lat) { skip += 1 nds[i].Id = osmcache.SKIP } else { hit += 1 } } } cache.Coords.PutCoords(nds) progress.AddCoords(len(nds)) } waitWriter.Done() }() } for i := 0; int64(i) < nNodes; i++ { waitWriter.Add(1) go func() { g := geos.NewGeos() defer g.Finish() m := tagmapping.NodeTagFilter() for nds := range nodes { if nds == nil { coordsSync.Sync() continue } numWithTags := 0 for i, _ := range nds { m.Filter(&nds[i].Tags) if len(nds[i].Tags) > 0 { numWithTags += 1 } if withLimiter { nd := element.Node{Long: nds[i].Long, Lat: nds[i].Lat} proj.NodeToMerc(&nd) if !limiter.IntersectsBuffer(g, nd.Long, nd.Lat) { nds[i].Id = osmcache.SKIP } } } cache.Nodes.PutNodes(nds) progress.AddNodes(numWithTags) } waitWriter.Done() }() } parser.Close() close(relations) close(ways) close(nodes) close(coords) waitWriter.Wait() }