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 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 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 destroyRings(g *geos.Geos, rings []*Ring) { for _, r := range rings { if r.geom != nil { g.Destroy(r.geom) r.geom = 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 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 }
// 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{} }
// 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 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 }