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) ([]Feature, error) { decoder := json.NewDecoder(r) obj := &object{} err := decoder.Decode(obj) if err != nil { return nil, err } polygons, err := constructPolygonFeatures(obj) if err != nil { return nil, err } g := geos.NewGeos() defer g.Finish() result := []Feature{} for _, p := range polygons { geom, err := geosPolygon(g, p.polygon) if err != nil { return nil, err } result = append(result, Feature{geom, p.properties}) } return result, err }
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 } ww.NodesToSrid(w.Nodes) w.Id = ww.wayId(w.Id) inserted := false if matches := ww.lineMatcher.MatchWay(w); len(matches) > 0 { 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 matches := ww.polygonMatcher.MatchWay(w); len(matches) > 0 { 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 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 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 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 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 matches := nw.pointMatcher.MatchNode(n); len(matches) > 0 { nw.NodeToSrid(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 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 TestClipperWithBuffer(t *testing.T) { g := geos.NewGeos() defer g.Finish() limiter, err := NewFromGeoJSON("./clipping.geojson", 0.1, 3857) if err != nil { t.Fatal(err) } if limiter.IntersectsBuffer(g, 9.94, 53.53) != true { t.Fatal() } if limiter.IntersectsBuffer(g, 9.04, 53.53) != false { t.Fatal() } }
// Build creates the (multi)polygon Geometry of the Relation. func (prep *PreparedRelation) Build() (Geometry, error) { g := geos.NewGeos() g.SetHandleSrid(prep.srid) defer g.Finish() geom, err := buildRelGeometry(g, prep.rel, prep.rings) if err != nil { return Geometry{}, err } wkb := g.AsEwkbHex(geom) if wkb == nil { return Geometry{}, errors.New("unable to create WKB for relation") } return Geometry{Geom: geom, Wkb: wkb}, nil }
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 TestSingleTable_Prepare(t *testing.T) { ts.dir = "/tmp/imposm3test" ts.config = importConfig{ connection: "postgis://", cacheDir: ts.dir, osmFileName: "build/single_table.pbf", mappingFileName: "single_table_mapping.json", } ts.g = geos.NewGeos() var err error ts.db, err = sql.Open("postgres", "sslmode=disable") if err != nil { t.Fatal(err) } ts.dropSchemas() }
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 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 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) } }
// Clip returns geom (in targetSRID) clipped to the LimitTo geometry. // Returns nil if geom is outside of the LimitTo geometry. // Returns only similar geometry types (e.g. clipped Polygon will return // one or more Polygons, but no LineString or Point, etc.) 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.IndexQueryGeoms(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 BenchmarkClipper(b *testing.B) { g := geos.NewGeos() defer g.Finish() limiter, err := NewFromGeoJSON("./clipping.geojson", 1.0, 3857) 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 (s *importTestSuite) queryGeom(t *testing.T, table string, id int64) *geos.Geom { stmt := fmt.Sprintf(`SELECT osm_id, ST_AsText(geometry) FROM "%s"."%s" WHERE osm_id=$1`, dbschemaProduction, table) row := s.db.QueryRow(stmt, id) r := record{} if err := row.Scan(&r.id, &r.wkt); err != nil { if err == sql.ErrNoRows { r.missing = true } else { t.Fatal(err) } } g := geos.NewGeos() defer g.Finish() geom := g.FromWkt(r.wkt) if geom == nil { t.Fatalf("unable to read WKT for %s", id) } return geom }
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) } }
func TestClipper(t *testing.T) { g := geos.NewGeos() defer g.Finish() limiter, err := NewFromGeoJSON("./clipping.geojson", 0.0, 3857) 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 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 TestRouteRelation_Prepare(t *testing.T) { var err error ts.dir, err = ioutil.TempDir("", "imposm3test") if err != nil { t.Fatal(err) } ts.config = importConfig{ connection: "postgis://", cacheDir: ts.dir, osmFileName: "build/route_relation.pbf", mappingFileName: "route_relation_mapping.yml", } ts.g = geos.NewGeos() ts.db, err = sql.Open("postgres", "sslmode=disable") if err != nil { t.Fatal(err) } ts.dropSchemas() }
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 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 BuildRings(rel *element.Relation) ([]*Ring, error) { var rings []*Ring var incompleteRings []*Ring var completeRings []*Ring var mergedRings []*Ring var err error g := geos.NewGeos() defer g.Finish() defer func() { if err != nil { destroyRings(g, mergedRings) destroyRings(g, completeRings) } }() // create rings for all WAY members for _, member := range rel.Members { if member.Way == nil { continue } rings = append(rings, NewRing(member.Way)) } // create geometries for closed rings, collect incomplete rings for _, r := range rings { if r.IsClosed() { r.geom, err = Polygon(g, r.nodes) if err != nil { return nil, err } completeRings = append(completeRings, r) } else { incompleteRings = append(incompleteRings, r) } } // merge incomplete rings mergedRings = mergeRings(incompleteRings) if len(completeRings)+len(mergedRings) == 0 { err = ErrorNoRing // for defer return nil, err } // create geometries for merged rings for _, ring := range mergedRings { if !ring.IsClosed() { err = ErrorNoRing // for defer return nil, err } ring.geom, err = Polygon(g, ring.nodes) if err != nil { return nil, err } } 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 }
// 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 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) // wait for all coords/nodes to be processed before continuing with // ways. required for -limitto checks coordsSync := sync.WaitGroup{} parser.FinishedCoords(func() { for i := 0; int64(i) < nCoords; i++ { coords <- nil } for i := 0; int64(i) < nNodes; i++ { nodes <- nil } coordsSync.Wait() }) // wait for all ways to be processed before continuing with // relations. required for -limitto checks waysSync := sync.WaitGroup{} parser.FinishedWays(func() { for i := 0; int64(i) < nWays; i++ { ways <- nil } waysSync.Wait() }) waitWriter := sync.WaitGroup{} for i := 0; int64(i) < nWays; i++ { waysSync.Add(1) waitWriter.Add(1) go func() { var skip, hit int m := tagmapping.WayTagFilter() for ws := range ways { if ws == nil { waysSync.Done() waysSync.Wait() continue } if skipWays { continue } for i, _ := range ws { m.Filter(&ws[i].Tags) if withLimiter { cached, err := cache.Coords.FirstRefIsCached(ws[i].Refs) if err != nil { log.Errorf("error while checking for cached refs of way %d: %v", ws[i].Id, err) cached = true // don't skip in case of error } if cached { hit += 1 } else { ws[i].Id = osmcache.SKIP skip += 1 } } } err := cache.Ways.PutWays(ws) if err != nil { log.Errorf("error while caching ways: %v", err) } 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 { cached, err := cache.Ways.FirstMemberIsCached(rels[i].Members) if err != nil { log.Errorf("error while checking for cached members of relation %d: %v", rels[i].Id, err) cached = true // don't skip in case of error } if cached { hit += 1 } else { skip += 1 rels[i].Id = osmcache.SKIP } } } err := cache.Relations.PutRelations(rels) if err != nil { log.Errorf("error while caching relation: %v", err) } progress.AddRelations(numWithTags) } waitWriter.Done() }() } for i := 0; int64(i) < nCoords; i++ { coordsSync.Add(1) waitWriter.Add(1) go func() { var skip, hit int g := geos.NewGeos() defer g.Finish() for nds := range coords { if nds == nil { coordsSync.Done() coordsSync.Wait() continue } if withLimiter { for i, _ := range nds { if !limiter.IntersectsBuffer(g, nds[i].Long, nds[i].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++ { coordsSync.Add(1) waitWriter.Add(1) go func() { g := geos.NewGeos() defer g.Finish() m := tagmapping.NodeTagFilter() for nds := range nodes { if nds == nil { coordsSync.Done() coordsSync.Wait() continue } numWithTags := 0 for i, _ := range nds { m.Filter(&nds[i].Tags) if len(nds[i].Tags) > 0 { numWithTags += 1 } if withLimiter { if !limiter.IntersectsBuffer(g, nds[i].Long, nds[i].Lat) { nds[i].Id = osmcache.SKIP } } } cache.Nodes.PutNodes(nds) progress.AddNodes(numWithTags) } waitWriter.Done() }() } parser.Parse() waitWriter.Wait() }