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 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 }
// 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 }