Esempio n. 1
0
// 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
}