// mergeGeometries func mergeGeometries(g *geos.Geos, geoms []*geos.Geom, geomType string) []*geos.Geom { // intersections from multiple sub-polygons // try to merge them back to a single geometry if strings.HasSuffix(geomType, "Polygon") { polygons := flattenPolygons(g, geoms) polygon := g.UnionPolygons(polygons) if polygon == nil { return nil } g.DestroyLater(polygon) return []*geos.Geom{polygon} } else if strings.HasSuffix(geomType, "LineString") { linestrings := flattenLineStrings(g, geoms) linestrings = filterInvalidLineStrings(g, linestrings) if len(linestrings) == 0 { return nil } union := g.LineMerge(linestrings) for _, l := range union { g.DestroyLater(l) } return union } else if geomType == "Point" { if len(geoms) >= 1 { for _, p := range geoms { g.DestroyLater(p) } return geoms[0:1] } return nil } else { panic("unexpected geometry type" + geomType) } }
func Point(g *geos.Geos, node element.Node) (*geos.Geom, error) { geom := g.Point(node.Long, node.Lat) if geom == nil { return nil, newGeomError("couldn't create point", 1) } g.DestroyLater(geom) return geom, nil }
func destroyRings(g *geos.Geos, rings []*Ring) { for _, r := range rings { if r.geom != nil { g.Destroy(r.geom) r.geom = nil } } }
func AsGeomElement(g *geos.Geos, geom *geos.Geom) (Geometry, error) { wkb := g.AsEwkbHex(geom) if wkb == nil { return Geometry{}, errors.New("could not create wkb") } return Geometry{ Wkb: wkb, Geom: geom, }, nil }
func (ww *WayWriter) buildAndInsert(g *geos.Geos, w *element.Way, matches []mapping.Match, isPolygon bool) error { var err error var geosgeom *geos.Geom // make copy to avoid interference with polygon/linestring matches way := element.Way(*w) if isPolygon { geosgeom, err = geomp.Polygon(g, way.Nodes) if err == nil { geosgeom, err = g.MakeValid(geosgeom) } } else { geosgeom, err = geomp.LineString(g, way.Nodes) } if err != nil { return err } geom, err := geomp.AsGeomElement(g, geosgeom) if err != nil { return err } if ww.limiter != nil { parts, err := ww.limiter.Clip(geom.Geom) if err != nil { return err } for _, p := range parts { way := element.Way(*w) geom = geomp.Geometry{Geom: p, Wkb: g.AsEwkbHex(p)} if isPolygon { if err := ww.inserter.InsertPolygon(way.OSMElem, geom, matches); err != nil { return err } } else { if err := ww.inserter.InsertLineString(way.OSMElem, geom, matches); err != nil { return err } } } } else { if isPolygon { if err := ww.inserter.InsertPolygon(way.OSMElem, geom, matches); err != nil { return err } } else { if err := ww.inserter.InsertLineString(way.OSMElem, geom, matches); err != nil { return err } } } return nil }
func filterInvalidLineStrings(g *geos.Geos, geoms []*geos.Geom) []*geos.Geom { var result []*geos.Geom for _, geom := range geoms { if geom.Length() > 1e-9 { result = append(result, geom) } else { g.Destroy(geom) } } return result }
func Polygon(g *geos.Geos, nodes []element.Node) (*geos.Geom, error) { nodes = unduplicateNodes(nodes) if len(nodes) < 4 { return nil, ErrorNoRing } coordSeq, err := g.CreateCoordSeq(uint32(len(nodes)), 2) if err != nil { return nil, err } // coordSeq inherited by LinearRing, no destroy for i, nd := range nodes { err := coordSeq.SetXY(g, uint32(i), nd.Long, nd.Lat) if err != nil { return nil, err } } ring, err := coordSeq.AsLinearRing(g) if err != nil { // coordSeq gets Destroy by GEOS return nil, err } // ring inherited by Polygon, no destroy geom := g.Polygon(ring, nil) if geom == nil { g.Destroy(ring) return nil, errors.New("unable to create polygon") } g.DestroyLater(geom) return geom, nil }
func geosRing(g *geos.Geos, ls geojson.LineString) (*geos.Geom, error) { coordSeq, err := g.CreateCoordSeq(uint32(len(ls)), 2) if err != nil { return nil, err } // coordSeq inherited by LinearRing, no destroy for i, p := range ls { err := coordSeq.SetXY(g, uint32(i), p.Long, p.Lat) if err != nil { return nil, err } } ring, err := coordSeq.AsLinearRing(g) if err != nil { // coordSeq gets Destroy by GEOS return nil, err } return ring, nil }
func geosPolygon(g *geos.Geos, polygon geojson.Polygon) (*geos.Geom, error) { if len(polygon) == 0 { return nil, errors.New("empty polygon") } shell, err := geosRing(g, polygon[0]) if err != nil { return nil, err } holes := make([]*geos.Geom, len(polygon)-1) for i, ls := range polygon[1:] { hole, err := geosRing(g, ls) if err != nil { return nil, err } holes[i] = hole } geom := g.Polygon(shell, holes) if geom == nil { g.Destroy(shell) for _, hole := range holes { g.Destroy(hole) } return nil, errors.New("unable to create polygon") } return geom, nil }
func LineString(g *geos.Geos, nodes []element.Node) (*geos.Geom, error) { nodes = unduplicateNodes(nodes) if len(nodes) < 2 { return nil, ErrorOneNodeWay } coordSeq, err := g.CreateCoordSeq(uint32(len(nodes)), 2) if err != nil { return nil, err } // coordSeq inherited by LineString for i, nd := range nodes { coordSeq.SetXY(g, uint32(i), nd.Long, nd.Lat) } geom, err := coordSeq.AsLineString(g) if err != nil { // coordSeq gets Destroy by GEOS return nil, err } g.DestroyLater(geom) return geom, nil }
// IntersectsBuffer returns true if the point (EPSG:4326) intersects the buffered // LimitTo geometry. func (c *Limiter) IntersectsBuffer(g *geos.Geos, x, y float64) bool { if c.bufferedPrep == nil { return true } if x < c.bufferedBbox.MinX || y < c.bufferedBbox.MinY || x > c.bufferedBbox.MaxX || y > c.bufferedBbox.MaxY { return false } p := g.Point(x, y) if p == nil { return false } defer g.Destroy(p) c.bufferedPrepMu.Lock() defer c.bufferedPrepMu.Unlock() return g.PreparedIntersects(c.bufferedPrep, p) }
func filterGeometryByType(g *geos.Geos, geom *geos.Geom, targetType string) []*geos.Geom { // Filter (multi)geometry for compatible `geom_type`, // because we can't insert points into linestring tables for example geomType := g.Type(geom) if geomType == targetType { // same type is fine return []*geos.Geom{geom} } if geomType == "Polygon" && targetType == "MultiPolygon" { // multipolygon mappings also support polygons return []*geos.Geom{geom} } if geomType == "MultiPolygon" && targetType == "Polygon" { // polygon mappings should also support multipolygons return []*geos.Geom{geom} } if g.NumGeoms(geom) >= 1 { // GeometryCollection or MultiLineString? return list of geometries var geoms []*geos.Geom for _, part := range g.Geoms(geom) { // only parts with same type if g.Type(part) == targetType { geoms = append(geoms, g.Clone(part)) } } g.Destroy(geom) if len(geoms) != 0 { return geoms } return []*geos.Geom{} } g.Destroy(geom) return []*geos.Geom{} }
func splitPolygonAtGrid(g *geos.Geos, geom *geos.Geom, gridWidth, currentGridWidth float64) ([]*geos.Geom, error) { var result []*geos.Geom geomBounds := geom.Bounds() if geomBounds == geos.NilBounds { return nil, errors.New("couldn't create bounds for geom") } for _, bounds := range tileBounds(geom.Bounds(), currentGridWidth) { clipGeom := g.BoundsPolygon(bounds) if clipGeom == nil { return nil, errors.New("couldn't create bounds polygon") } part := g.Intersection(geom, clipGeom) g.Destroy(clipGeom) if part == nil { return nil, errors.New("couldn't create intersection") } if !g.IsEmpty(part) && strings.HasSuffix(g.Type(part), "Polygon") { if gridWidth >= currentGridWidth { result = append(result, part) } else { moreParts, err := splitPolygonAtGrid(g, part, gridWidth, currentGridWidth/2.0) g.Destroy(part) if err != nil { return nil, err } result = append(result, moreParts...) } } } return result, nil }
func flattenLineStrings(g *geos.Geos, geoms []*geos.Geom) []*geos.Geom { var result []*geos.Geom for _, geom := range geoms { if g.Type(geom) == "MultiLineString" { for _, part := range g.Geoms(geom) { result = append(result, g.Clone(part)) } g.Destroy(geom) } else if g.Type(geom) == "LineString" { result = append(result, geom) } else { log.Printf("unexpected geometry type in flattenLineStrings: %s", g.Type(geom)) g.Destroy(geom) } } return result }
// 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(g *geos.Geos, rel *element.Relation, rings []*ring) (*geos.Geom, error) { 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 rings[i].outer = false } else { shells[rings[j]] = true rings[i].outer = true } } } if rings[i].containedBy == -1 { // add as shell if it is not a hole shells[rings[i]] = true rings[i].outer = true } g.PreparedDestroy(testGeom) } var polygons []*geos.Geom for shell, _ := range shells { var interiors []*geos.Geom for hole, _ := range shell.holes { 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) } 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.") } } var err error result, err = g.MakeValid(result) if err != nil { return nil, err } g.DestroyLater(result) outer := make(map[int64]struct{}) for i := range rings { if rings[i].outer { for _, w := range rings[i].ways { outer[w.Id] = struct{}{} } } } for i := range rel.Members { mid := rel.Members[i].Id if _, ok := outer[mid]; ok { rel.Members[i].Role = "outer" } else { rel.Members[i].Role = "inner" } } return result, nil }
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 { if err != cache.NotFound { log.Warn(err) } return false } 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 } } rw.NodeToSrid(nd) r.Members[i].Node = nd r.Members[i].Elem = &nd.OSMElem } } 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 }
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) } return false } // check for matches befor building the geometry matches := rw.polygonMatcher.MatchRelation(r) if matches == nil { return false } // 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) } return false } if rw.limiter != nil { start := time.Now() parts, err := rw.limiter.Clip(geom.Geom) if err != nil { log.Warn(err) return false } 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 { rel := element.Relation(*r) rel.Id = rw.relId(r.Id) geom = geomp.Geometry{Geom: g, Wkb: geos.AsEwkbHex(g)} err := rw.inserter.InsertPolygon(rel.OSMElem, geom, matches) if err != nil { if errl, ok := err.(ErrorLevel); !ok || errl.Level() > 0 { log.Warn(err) } continue } } } 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 { log.Warn(err) } return false } } for _, m := range mapping.SelectRelationPolygons(rw.polygonMatcher, r) { err = rw.osmCache.InsertedWays.PutWay(m.Way) if err != nil { log.Warn(err) } } return true }