writer: do not use GEOS for LineString geometries
parent
a367850f6f
commit
ae4197a147
|
@ -85,10 +85,62 @@ func BenchmarkLineString(b *testing.B) {
|
||||||
nodes[i] = element.Node{Lat: 0, Long: float64(i)}
|
nodes[i] = element.Node{Lat: 0, Long: float64(i)}
|
||||||
}
|
}
|
||||||
g := geos.NewGeos()
|
g := geos.NewGeos()
|
||||||
|
g.SetHandleSrid(4326)
|
||||||
defer g.Finish()
|
defer g.Finish()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
LineString(g, nodes)
|
geom, err := LineString(g, nodes)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
g.AsEwkbHex(geom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkLineStringNoGeos(b *testing.B) {
|
||||||
|
size := 16
|
||||||
|
nodes := make([]element.Node, size)
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
nodes[i] = element.Node{Lat: 0, Long: float64(i)}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
NodesAsEWKBHexLineString(nodes, 4326)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkPolygon(b *testing.B) {
|
||||||
|
size := 16
|
||||||
|
nodes := make([]element.Node, size)
|
||||||
|
for i := 1; i < size; i++ {
|
||||||
|
nodes[i] = element.Node{Lat: 1, Long: float64(i)}
|
||||||
|
}
|
||||||
|
nodes[0] = element.Node{Lat: 0, Long: 0}
|
||||||
|
nodes[len(nodes)-1] = element.Node{Lat: 0, Long: 0}
|
||||||
|
g := geos.NewGeos()
|
||||||
|
g.SetHandleSrid(4326)
|
||||||
|
defer g.Finish()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
geom, err := Polygon(g, nodes)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
g.AsEwkbHex(geom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkPolygonNoGeos(b *testing.B) {
|
||||||
|
size := 16
|
||||||
|
nodes := make([]element.Node, size)
|
||||||
|
for i := 1; i < size; i++ {
|
||||||
|
nodes[i] = element.Node{Lat: 1, Long: float64(i)}
|
||||||
|
}
|
||||||
|
nodes[0] = element.Node{Lat: 0, Long: 0}
|
||||||
|
nodes[len(nodes)-1] = element.Node{Lat: 0, Long: 0}
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
NodesAsEWKBHexPolygon(nodes, 4326)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
package geom
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
|
||||||
|
"github.com/omniscale/imposm3/element"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
wkbSridFlag = 0x20000000
|
||||||
|
wkbLineStringType = 2
|
||||||
|
wkbPolygonType = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
func NodesAsEWKBHexLineString(nodes []element.Node, srid int) ([]byte, error) {
|
||||||
|
nodes = unduplicateNodes(nodes)
|
||||||
|
if len(nodes) < 2 {
|
||||||
|
return nil, ErrorOneNodeWay
|
||||||
|
}
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
binary.Write(buf, binary.LittleEndian, uint8(1)) // little endian
|
||||||
|
if srid != 0 {
|
||||||
|
binary.Write(buf, binary.LittleEndian, uint32(wkbLineStringType|wkbSridFlag))
|
||||||
|
binary.Write(buf, binary.LittleEndian, uint32(srid))
|
||||||
|
} else {
|
||||||
|
binary.Write(buf, binary.LittleEndian, uint32(wkbLineStringType))
|
||||||
|
}
|
||||||
|
binary.Write(buf, binary.LittleEndian, uint32(len(nodes)))
|
||||||
|
|
||||||
|
for _, nd := range nodes {
|
||||||
|
binary.Write(buf, binary.LittleEndian, nd.Long)
|
||||||
|
binary.Write(buf, binary.LittleEndian, nd.Lat)
|
||||||
|
}
|
||||||
|
|
||||||
|
src := buf.Bytes()
|
||||||
|
dst := make([]byte, hex.EncodedLen(len(src)))
|
||||||
|
hex.Encode(dst, src)
|
||||||
|
return dst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NodesAsEWKBHexPolygon(nodes []element.Node, srid int) ([]byte, error) {
|
||||||
|
// TODO undup nodes and check if closed
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
binary.Write(buf, binary.LittleEndian, uint8(1)) // little endian
|
||||||
|
if srid != 0 {
|
||||||
|
binary.Write(buf, binary.LittleEndian, uint32(wkbPolygonType|wkbSridFlag))
|
||||||
|
binary.Write(buf, binary.LittleEndian, uint32(srid))
|
||||||
|
} else {
|
||||||
|
binary.Write(buf, binary.LittleEndian, uint32(wkbPolygonType))
|
||||||
|
}
|
||||||
|
binary.Write(buf, binary.LittleEndian, uint32(1)) // one ring
|
||||||
|
binary.Write(buf, binary.LittleEndian, uint32(len(nodes)))
|
||||||
|
|
||||||
|
for _, nd := range nodes {
|
||||||
|
binary.Write(buf, binary.LittleEndian, nd.Long)
|
||||||
|
binary.Write(buf, binary.LittleEndian, nd.Lat)
|
||||||
|
}
|
||||||
|
|
||||||
|
src := buf.Bytes()
|
||||||
|
dst := make([]byte, hex.EncodedLen(len(src)))
|
||||||
|
hex.Encode(dst, src)
|
||||||
|
return dst, nil
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
package geom
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/omniscale/imposm3/element"
|
||||||
|
"github.com/omniscale/imposm3/geom/geos"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWkbLineString(t *testing.T) {
|
||||||
|
nodes := make([]element.Node, 5)
|
||||||
|
nodes[0] = element.Node{Lat: 0, Long: 0}
|
||||||
|
nodes[1] = element.Node{Lat: 1.123, Long: -0.2}
|
||||||
|
nodes[2] = element.Node{Lat: 1.99, Long: 1}
|
||||||
|
nodes[3] = element.Node{Lat: 0, Long: 1.1}
|
||||||
|
nodes[4] = element.Node{Lat: 0, Long: 0}
|
||||||
|
g := geos.NewGeos()
|
||||||
|
defer g.Finish()
|
||||||
|
|
||||||
|
geom, err := LineString(g, nodes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
geosWkb := string(g.AsEwkbHex(geom))
|
||||||
|
wkbb, err := NodesAsEWKBHexLineString(nodes, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
wkb := strings.ToUpper(string(wkbb))
|
||||||
|
|
||||||
|
if geosWkb != wkb {
|
||||||
|
t.Error("linestring wkb differs")
|
||||||
|
t.Error(string(geosWkb))
|
||||||
|
t.Error(string(wkb))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWkbPolygon(t *testing.T) {
|
||||||
|
nodes := make([]element.Node, 5)
|
||||||
|
nodes[0] = element.Node{Lat: 1.123, Long: -0.2}
|
||||||
|
nodes[1] = element.Node{Lat: 1.99, Long: 1}
|
||||||
|
nodes[2] = element.Node{Lat: 0, Long: 1.1}
|
||||||
|
nodes[3] = element.Node{Lat: 0, Long: 0}
|
||||||
|
nodes[4] = element.Node{Lat: 1.123, Long: -0.2}
|
||||||
|
g := geos.NewGeos()
|
||||||
|
defer g.Finish()
|
||||||
|
|
||||||
|
geom, err := Polygon(g, nodes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
geosWkb := string(g.AsEwkbHex(geom))
|
||||||
|
wkbb, err := NodesAsEWKBHexPolygon(nodes, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
wkb := strings.ToUpper(string(wkbb))
|
||||||
|
|
||||||
|
if geosWkb != wkb {
|
||||||
|
t.Error("polygon wkb differs")
|
||||||
|
t.Error(string(geosWkb))
|
||||||
|
t.Error(string(wkb))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkAsWkb(b *testing.B) {
|
||||||
|
g := geos.NewGeos()
|
||||||
|
defer g.Finish()
|
||||||
|
|
||||||
|
p := g.FromWkt("LINESTRING(0 0, 5 0, 10 0, 10 5, 10 10, 0 10, 0 0)")
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
g.AsEwkbHex(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkAsWkbSrid(b *testing.B) {
|
||||||
|
g := geos.NewGeos()
|
||||||
|
g.SetHandleSrid(4326)
|
||||||
|
defer g.Finish()
|
||||||
|
|
||||||
|
p := g.FromWkt("LINESTRING(0 0, 5 0, 10 0, 10 5, 10 10, 0 10, 0 0)")
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
g.AsEwkbHex(p)
|
||||||
|
}
|
||||||
|
}
|
|
@ -130,12 +130,44 @@ func (ww *WayWriter) loop() {
|
||||||
ww.wg.Done()
|
ww.wg.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ww *WayWriter) buildAndInsert(g *geos.Geos, w *element.Way, matches []mapping.Match, isPolygon bool) (error, bool) {
|
func (ww *WayWriter) buildAndInsert(
|
||||||
var err error
|
g *geos.Geos,
|
||||||
var geosgeom *geos.Geom
|
w *element.Way,
|
||||||
|
matches []mapping.Match,
|
||||||
|
isPolygon bool,
|
||||||
|
) (error, bool) {
|
||||||
|
|
||||||
// make copy to avoid interference with polygon/linestring matches
|
// make copy to avoid interference with polygon/linestring matches
|
||||||
way := element.Way(*w)
|
way := element.Way(*w)
|
||||||
|
|
||||||
|
// Shortcut for non-clipped LineStrings:
|
||||||
|
// We don't need any function from GEOS, so we can directly create the WKB hex string.
|
||||||
|
if ww.limiter == nil && !isPolygon {
|
||||||
|
wkb, err := geomp.NodesAsEWKBHexLineString(w.Nodes, ww.srid)
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
geom := geomp.Geometry{Wkb: wkb}
|
||||||
|
if err := ww.inserter.InsertLineString(w.OSMElem, geom, matches); err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
// TODO: We could also apply this shortcut for simple Polygons that we we don't
|
||||||
|
// need to make valid (e.g. no holes, only 4 points).
|
||||||
|
// However, this does not work with (Webmerc)Area columns, unless we have
|
||||||
|
// a non-GEOS implementation.
|
||||||
|
// if ww.limiter == nil && isPolygon && len(w.Nodes) <= 5 {
|
||||||
|
// geom := geomp.Geometry{Wkb: geomp.WayAsEWKBHexPolygon(w.Nodes, ww.srid)}
|
||||||
|
// if err := ww.inserter.InsertPolygon(w.OSMElem, geom, matches); err != nil {
|
||||||
|
// return err, false
|
||||||
|
// }
|
||||||
|
// return nil, true
|
||||||
|
// }
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var geosgeom *geos.Geom
|
||||||
|
|
||||||
if isPolygon {
|
if isPolygon {
|
||||||
geosgeom, err = geomp.Polygon(g, way.Nodes)
|
geosgeom, err = geomp.Polygon(g, way.Nodes)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
Loading…
Reference in New Issue