コード例 #1
0
ファイル: limit.go プロジェクト: Rachine/imposm3
// 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)
	}
}
コード例 #2
0
ファイル: geom.go プロジェクト: Rachine/imposm3
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
}
コード例 #3
0
ファイル: multipolygon.go プロジェクト: rmarianski/imposm3
func destroyRings(g *geos.Geos, rings []*Ring) {
	for _, r := range rings {
		if r.geom != nil {
			g.Destroy(r.geom)
			r.geom = nil
		}
	}
}
コード例 #4
0
ファイル: geom.go プロジェクト: Rachine/imposm3
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
}
コード例 #5
0
ファイル: ways.go プロジェクト: Rachine/imposm3
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
}
コード例 #6
0
ファイル: limit.go プロジェクト: Rachine/imposm3
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
}
コード例 #7
0
ファイル: geom.go プロジェクト: Rachine/imposm3
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
}
コード例 #8
0
ファイル: limit.go プロジェクト: Rachine/imposm3
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
}
コード例 #9
0
ファイル: limit.go プロジェクト: Rachine/imposm3
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
}
コード例 #10
0
ファイル: geom.go プロジェクト: Rachine/imposm3
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
}
コード例 #11
0
ファイル: limit.go プロジェクト: Rachine/imposm3
// 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)
}
コード例 #12
0
ファイル: limit.go プロジェクト: Rachine/imposm3
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{}
}
コード例 #13
0
ファイル: limit.go プロジェクト: Rachine/imposm3
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
}
コード例 #14
0
ファイル: limit.go プロジェクト: Rachine/imposm3
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
}
コード例 #15
0
ファイル: multipolygon.go プロジェクト: Rachine/imposm3
// 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
}
コード例 #16
0
ファイル: relations.go プロジェクト: Rachine/imposm3
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
}
コード例 #17
0
ファイル: relations.go プロジェクト: Rachine/imposm3
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
}