package geom import ( "errors" "math" "github.com/omniscale/imposm3/element" "github.com/omniscale/imposm3/geom/geos" ) type GeomError struct { message string level int } type Geometry struct { Geom *geos.Geom Wkb []byte } func (e *GeomError) Error() string { return e.message } func (e *GeomError) Level() int { return e.level } func newGeomError(message string, level int) *GeomError { return &GeomError{message, level} } var ( ErrorOneNodeWay = newGeomError("need at least two separate nodes for way", 0) ErrorNoRing = newGeomError("linestrings do not form ring", 0) ) 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 } func nodesEqual(a, b element.Node) bool { if d := a.Long - b.Long; math.Abs(d) < 1e-9 { if d := a.Lat - b.Lat; math.Abs(d) < 1e-9 { return true } } return false } func unduplicateNodes(nodes []element.Node) []element.Node { if len(nodes) < 2 { return nodes } foundDup := false for i := 1; i < len(nodes); i++ { if nodesEqual(nodes[i-1], nodes[i]) { foundDup = true break } } if !foundDup { return nodes } result := make([]element.Node, 0, len(nodes)) result = append(result, nodes[0]) for i := 1; i < len(nodes); i++ { if nodesEqual(nodes[i-1], nodes[i]) { continue } result = append(result, nodes[i]) } return result } 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 } 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 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 }