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