Merge branch 'ogr-replace'
commit
da9595d970
|
@ -54,7 +54,7 @@ Features
|
||||||
Limit imported geometries to polygons from Shapefiles or GeoJSON, for city/state/country imports.
|
Limit imported geometries to polygons from Shapefiles or GeoJSON, for city/state/country imports.
|
||||||
|
|
||||||
- Easy deployment:
|
- Easy deployment:
|
||||||
Single binary with only runtime dependencies to common libs (GEOS, GDAL, SQLite and LevelDB)
|
Single binary with only runtime dependencies to common libs (GEOS, SQLite and LevelDB)
|
||||||
|
|
||||||
- Support for table namespace (PostgreSQL schema)
|
- Support for table namespace (PostgreSQL schema)
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ You need [Go >=1.1](http://golang.org).
|
||||||
|
|
||||||
#### C/C++ libraries
|
#### C/C++ libraries
|
||||||
|
|
||||||
Other dependencies are [libleveldb][], [libgeos][], [libgdal with OGR][libgdal] and [libsqlite3][].
|
Other dependencies are [libleveldb][], [libgeos][] and [libsqlite3][].
|
||||||
Imposm 3 was tested with recent versions of these libraries, but you might succeed with older versions.
|
Imposm 3 was tested with recent versions of these libraries, but you might succeed with older versions.
|
||||||
GEOS >=3.2 is recommended, since it became much more robust when handling invalid geometries.
|
GEOS >=3.2 is recommended, since it became much more robust when handling invalid geometries.
|
||||||
For best performance use [HyperLevelDB][libhyperleveldb] as an in-place replacement for libleveldb.
|
For best performance use [HyperLevelDB][libhyperleveldb] as an in-place replacement for libleveldb.
|
||||||
|
@ -135,7 +135,6 @@ For best performance use [HyperLevelDB][libhyperleveldb] as an in-place replacem
|
||||||
[libleveldb]: https://code.google.com/p/leveldb/
|
[libleveldb]: https://code.google.com/p/leveldb/
|
||||||
[libhyperleveldb]: https://github.com/rescrv/HyperLevelDB
|
[libhyperleveldb]: https://github.com/rescrv/HyperLevelDB
|
||||||
[libgeos]: http://trac.osgeo.org/geos/
|
[libgeos]: http://trac.osgeo.org/geos/
|
||||||
[libgdal]: http://www.gdal.org/
|
|
||||||
[libsqlite3]: http://www.sqlite.org/
|
[libsqlite3]: http://www.sqlite.org/
|
||||||
|
|
||||||
#### Go libraries
|
#### Go libraries
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
/*
|
||||||
|
Package geojson creates GEOS geometries from GeoJSON files.
|
||||||
|
*/
|
||||||
|
package geojson
|
|
@ -0,0 +1,253 @@
|
||||||
|
package geojson
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"imposm3/geom/geos"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type object struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Features []object `json:"features"`
|
||||||
|
Geometry *object `json:"geometry"`
|
||||||
|
Coordinates []interface{} `json:"coordinates"`
|
||||||
|
Properties map[string]interface{} `json:"properties"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type geometry struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Coordinates []interface{} `json:"coordinates"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type point struct {
|
||||||
|
long float64
|
||||||
|
lat float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPointFromCoords(coords []interface{}) (point, error) {
|
||||||
|
p := point{}
|
||||||
|
if len(coords) != 2 {
|
||||||
|
return p, errors.New("point list length not 2")
|
||||||
|
}
|
||||||
|
var ok bool
|
||||||
|
p.long, ok = coords[0].(float64)
|
||||||
|
if !ok {
|
||||||
|
return p, errors.New("invalid lon")
|
||||||
|
}
|
||||||
|
p.lat, ok = coords[1].(float64)
|
||||||
|
if !ok {
|
||||||
|
return p, errors.New("invalid lat")
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type lineString []point
|
||||||
|
|
||||||
|
func newLineStringFromCoords(coords []interface{}) (lineString, error) {
|
||||||
|
ls := lineString{}
|
||||||
|
|
||||||
|
for _, part := range coords {
|
||||||
|
coord, ok := part.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return ls, errors.New("point not a list")
|
||||||
|
}
|
||||||
|
p, err := newPointFromCoords(coord)
|
||||||
|
if err != nil {
|
||||||
|
return ls, err
|
||||||
|
}
|
||||||
|
ls = append(ls, p)
|
||||||
|
}
|
||||||
|
return ls, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type polygon []lineString
|
||||||
|
|
||||||
|
func newPolygonFromCoords(coords []interface{}) (polygon, error) {
|
||||||
|
poly := polygon{}
|
||||||
|
|
||||||
|
for _, part := range coords {
|
||||||
|
lsCoords, ok := part.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return poly, errors.New("polygon lineString not a list")
|
||||||
|
}
|
||||||
|
ls, err := newLineStringFromCoords(lsCoords)
|
||||||
|
if err != nil {
|
||||||
|
return poly, err
|
||||||
|
}
|
||||||
|
poly = append(poly, ls)
|
||||||
|
}
|
||||||
|
return poly, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMultiPolygonFromCoords(coords []interface{}) ([]polygon, error) {
|
||||||
|
mp := []polygon{}
|
||||||
|
|
||||||
|
for _, part := range coords {
|
||||||
|
polyCoords, ok := part.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return mp, errors.New("multipolygon polygon not a list")
|
||||||
|
}
|
||||||
|
poly, err := newPolygonFromCoords(polyCoords)
|
||||||
|
if err != nil {
|
||||||
|
return mp, err
|
||||||
|
}
|
||||||
|
mp = append(mp, poly)
|
||||||
|
}
|
||||||
|
return mp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseGeoJson(r io.Reader) ([]*geos.Geom, error) {
|
||||||
|
decoder := json.NewDecoder(r)
|
||||||
|
|
||||||
|
obj := &object{}
|
||||||
|
err := decoder.Decode(obj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
polygons, err := constructPolygons(obj)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
g := geos.NewGeos()
|
||||||
|
defer g.Finish()
|
||||||
|
result := []*geos.Geom{}
|
||||||
|
|
||||||
|
for _, p := range polygons {
|
||||||
|
geom, err := geosPolygon(g, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, geom)
|
||||||
|
|
||||||
|
}
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func constructPolygons(obj *object) ([]polygon, error) {
|
||||||
|
switch obj.Type {
|
||||||
|
case "Point":
|
||||||
|
return nil, errors.New("only polygon or MultiPolygon are supported")
|
||||||
|
case "LineString":
|
||||||
|
return nil, errors.New("only polygon or MultiPolygon are supported")
|
||||||
|
case "Polygon":
|
||||||
|
poly, err := newPolygonFromCoords(obj.Coordinates)
|
||||||
|
return []polygon{poly}, err
|
||||||
|
case "MultiPolygon":
|
||||||
|
poly, err := newMultiPolygonFromCoords(obj.Coordinates)
|
||||||
|
return poly, err
|
||||||
|
case "Feature":
|
||||||
|
geom, err := constructPolygons(obj.Geometry)
|
||||||
|
return geom, err
|
||||||
|
case "FeatureCollection":
|
||||||
|
features := make([]polygon, 0)
|
||||||
|
|
||||||
|
for _, obj := range obj.Features {
|
||||||
|
geom, err := constructPolygons(&obj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
features = append(features, geom...)
|
||||||
|
}
|
||||||
|
return features, nil
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unknown type: " + obj.Type)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeoJson struct {
|
||||||
|
object object
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGeoJson(r io.Reader) (*GeoJson, error) {
|
||||||
|
result := &GeoJson{}
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(r)
|
||||||
|
|
||||||
|
err := decoder.Decode(&result.object)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gj *GeoJson) Geoms() ([]*geos.Geom, error) {
|
||||||
|
|
||||||
|
polygons, err := constructPolygons(&gj.object)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
g := geos.NewGeos()
|
||||||
|
defer g.Finish()
|
||||||
|
result := []*geos.Geom{}
|
||||||
|
|
||||||
|
for _, p := range polygons {
|
||||||
|
geom, err := geosPolygon(g, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, geom)
|
||||||
|
|
||||||
|
}
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func geosRing(g *geos.Geos, ls 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func geosPolygon(g *geos.Geos, polygon 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")
|
||||||
|
}
|
||||||
|
g.DestroyLater(geom)
|
||||||
|
return geom, nil
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
package geojson
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParsePolygon(t *testing.T) {
|
||||||
|
r := bytes.NewBufferString(`{"type": "Polygon", "coordinates": [[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]]]}`)
|
||||||
|
geoms, err := ParseGeoJson(r)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(geoms) != 1 {
|
||||||
|
t.Fatal(geoms)
|
||||||
|
}
|
||||||
|
|
||||||
|
if math.Abs(geoms[0].Area()-100) > 0.00001 {
|
||||||
|
t.Fatal(geoms[0].Area())
|
||||||
|
}
|
||||||
|
|
||||||
|
r = bytes.NewBufferString(`{"type": "Polygon", "coordinates": [[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]], [[5, 5], [6, 5], [6, 6], [5, 6], [5, 5]]]}`)
|
||||||
|
geoms, err = ParseGeoJson(r)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(geoms) != 1 {
|
||||||
|
t.Fatal(geoms)
|
||||||
|
}
|
||||||
|
|
||||||
|
if math.Abs(geoms[0].Area()-99) > 0.00001 {
|
||||||
|
t.Fatal(geoms[0].Area())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseMultiPolygon(t *testing.T) {
|
||||||
|
r := bytes.NewBufferString(`{"type": "MultiPolygon", "coordinates":
|
||||||
|
[[[[0, 0], [10, 0], [10, 10], [0, 0]]],
|
||||||
|
[[[0, 0], [10, 0], [10, 10], [0, 0]]]]
|
||||||
|
}`)
|
||||||
|
geoms, err := ParseGeoJson(r)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(geoms) != 2 {
|
||||||
|
t.Fatal(geoms)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseFeature(t *testing.T) {
|
||||||
|
r := bytes.NewBufferString(`{"type": "Feature", "geometry": {
|
||||||
|
"type": "Polygon", "coordinates": [[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]]]
|
||||||
|
}}`)
|
||||||
|
geoms, err := ParseGeoJson(r)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(geoms) != 1 {
|
||||||
|
t.Fatal(geoms)
|
||||||
|
}
|
||||||
|
if math.Abs(geoms[0].Area()-100) > 0.00001 {
|
||||||
|
t.Fatal(geoms[0].Area())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseFeatureCollection(t *testing.T) {
|
||||||
|
r := bytes.NewBufferString(`{"type": "FeatureCollection", "features": [
|
||||||
|
{"type": "Feature", "geometry":
|
||||||
|
{"type": "Polygon", "coordinates": [[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]]]}
|
||||||
|
},
|
||||||
|
{"type": "Feature", "geometry":
|
||||||
|
{"type": "Polygon", "coordinates": [[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]]]}
|
||||||
|
}
|
||||||
|
]}`)
|
||||||
|
geoms, err := ParseGeoJson(r)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(geoms) != 2 {
|
||||||
|
t.Fatal(geoms)
|
||||||
|
}
|
||||||
|
if math.Abs(geoms[0].Area()-100) > 0.00001 {
|
||||||
|
t.Fatal(geoms[0].Area())
|
||||||
|
}
|
||||||
|
}
|
|
@ -121,7 +121,7 @@ func Polygon(g *geos.Geos, nodes []element.Node) (*geos.Geom, error) {
|
||||||
}
|
}
|
||||||
// ring inherited by Polygon, no destroy
|
// ring inherited by Polygon, no destroy
|
||||||
|
|
||||||
geom := g.CreatePolygon(ring, nil)
|
geom := g.Polygon(ring, nil)
|
||||||
if geom == nil {
|
if geom == nil {
|
||||||
g.Destroy(ring)
|
g.Destroy(ring)
|
||||||
return nil, errors.New("unable to create polygon")
|
return nil, errors.New("unable to create polygon")
|
||||||
|
|
|
@ -104,17 +104,6 @@ func (this *Geos) SetHandleSrid(srid int) {
|
||||||
this.srid = srid
|
this.srid = srid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Geos) CreatePolygon(shell *Geom, holes []*Geom) *Geom {
|
|
||||||
if len(holes) > 0 {
|
|
||||||
panic("holes not implemented")
|
|
||||||
}
|
|
||||||
polygon := C.GEOSGeom_createPolygon_r(this.v, shell.v, nil, 0)
|
|
||||||
if polygon == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &Geom{polygon}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Geos) NumGeoms(geom *Geom) int32 {
|
func (this *Geos) NumGeoms(geom *Geom) int32 {
|
||||||
count := int32(C.GEOSGetNumGeometries_r(this.v, geom.v))
|
count := int32(C.GEOSGetNumGeometries_r(this.v, geom.v))
|
||||||
return count
|
return count
|
||||||
|
@ -170,7 +159,7 @@ func (this *Geos) BoundsPolygon(bounds Bounds) *Geom {
|
||||||
}
|
}
|
||||||
// geom inherited by Polygon, no destroy
|
// geom inherited by Polygon, no destroy
|
||||||
|
|
||||||
geom = this.CreatePolygon(geom, nil)
|
geom = this.Polygon(geom, nil)
|
||||||
this.DestroyLater(geom)
|
this.DestroyLater(geom)
|
||||||
return geom
|
return geom
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,11 @@ package limit
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"imposm3/geom/geojson"
|
||||||
"imposm3/geom/geos"
|
"imposm3/geom/geos"
|
||||||
"imposm3/geom/ogr"
|
|
||||||
"imposm3/logging"
|
"imposm3/logging"
|
||||||
"math"
|
"math"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
@ -58,7 +59,12 @@ func splitParams(bounds geos.Bounds, maxGrids int, minGridWidth float64) (float6
|
||||||
}
|
}
|
||||||
|
|
||||||
gridWidth := math.Max(gridWidthX, gridWidthY)
|
gridWidth := math.Max(gridWidthX, gridWidthY)
|
||||||
return gridWidth, gridWidth * 100
|
currentWidth := gridWidth
|
||||||
|
|
||||||
|
for currentWidth <= width/2 {
|
||||||
|
currentWidth *= 2
|
||||||
|
}
|
||||||
|
return gridWidth, currentWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
func SplitPolygonAtAutoGrid(g *geos.Geos, geom *geos.Geom) ([]*geos.Geom, error) {
|
func SplitPolygonAtAutoGrid(g *geos.Geos, geom *geos.Geom) ([]*geos.Geom, error) {
|
||||||
|
@ -89,7 +95,7 @@ func SplitPolygonAtGrid(g *geos.Geos, geom *geos.Geom, gridWidth, currentGridWid
|
||||||
if gridWidth >= currentGridWidth {
|
if gridWidth >= currentGridWidth {
|
||||||
result = append(result, part)
|
result = append(result, part)
|
||||||
} else {
|
} else {
|
||||||
moreParts, err := SplitPolygonAtGrid(g, part, gridWidth, currentGridWidth/10.0)
|
moreParts, err := SplitPolygonAtGrid(g, part, gridWidth, currentGridWidth/2.0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -107,12 +113,22 @@ type Limiter struct {
|
||||||
bufferedBbox geos.Bounds
|
bufferedBbox geos.Bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFromOgrSource(source string) (*Limiter, error) {
|
func NewFromGeoJson(source string) (*Limiter, error) {
|
||||||
return NewFromOgrSourceWithBuffered(source, 0.0)
|
return NewFromGeoJsonWithBuffered(source, 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFromOgrSourceWithBuffered(source string, buffer float64) (*Limiter, error) {
|
func NewFromGeoJsonWithBuffered(source string, buffer float64) (*Limiter, error) {
|
||||||
ds, err := ogr.Open(source)
|
|
||||||
|
f, err := os.Open(source)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gj, err := geojson.NewGeoJson(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
geoms, err := gj.Geoms()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -120,11 +136,6 @@ func NewFromOgrSourceWithBuffered(source string, buffer float64) (*Limiter, erro
|
||||||
g := geos.NewGeos()
|
g := geos.NewGeos()
|
||||||
defer g.Finish()
|
defer g.Finish()
|
||||||
|
|
||||||
layer, err := ds.Layer()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
index := g.CreateIndex()
|
index := g.CreateIndex()
|
||||||
|
|
||||||
var polygons []*geos.Geom
|
var polygons []*geos.Geom
|
||||||
|
@ -134,7 +145,7 @@ func NewFromOgrSourceWithBuffered(source string, buffer float64) (*Limiter, erro
|
||||||
withBuffer = true
|
withBuffer = true
|
||||||
}
|
}
|
||||||
|
|
||||||
for geom := range layer.Geoms() {
|
for _, geom := range geoms {
|
||||||
if withBuffer {
|
if withBuffer {
|
||||||
simplified := g.SimplifyPreserveTopology(geom, 1000)
|
simplified := g.SimplifyPreserveTopology(geom, 1000)
|
||||||
if simplified == nil {
|
if simplified == nil {
|
||||||
|
|
|
@ -25,9 +25,9 @@ func TestSplitPolygonAtGrids(t *testing.T) {
|
||||||
expected := []geos.Bounds{
|
expected := []geos.Bounds{
|
||||||
{0, 0, 0.05, 0.05},
|
{0, 0, 0.05, 0.05},
|
||||||
{0, 0.05, 0.05, 0.1},
|
{0, 0.05, 0.05, 0.1},
|
||||||
{0, 0.1, 0.05, 0.11},
|
|
||||||
{0.05, 0, 0.1, 0.05},
|
{0.05, 0, 0.1, 0.05},
|
||||||
{0.05, 0.05, 0.1, 0.1},
|
{0.05, 0.05, 0.1, 0.1},
|
||||||
|
{0, 0.1, 0.05, 0.11},
|
||||||
{0.05, 0.1, 0.1, 0.11},
|
{0.05, 0.1, 0.1, 0.11},
|
||||||
{0.1, 0, 0.15, 0.05},
|
{0.1, 0, 0.15, 0.05},
|
||||||
{0.1, 0.05, 0.15, 0.1},
|
{0.1, 0.05, 0.15, 0.1},
|
||||||
|
@ -39,7 +39,10 @@ func TestSplitPolygonAtGrids(t *testing.T) {
|
||||||
|
|
||||||
geom := g.BoundsPolygon(geos.Bounds{0, 0, 0.15, 0.11})
|
geom := g.BoundsPolygon(geos.Bounds{0, 0, 0.15, 0.11})
|
||||||
|
|
||||||
geoms, _ := SplitPolygonAtGrid(g, geom, 0.05, 5.0)
|
geoms, _ := SplitPolygonAtGrid(g, geom, 0.05, 0.2)
|
||||||
|
for _, geom := range geoms {
|
||||||
|
t.Log(geom.Bounds())
|
||||||
|
}
|
||||||
for i, geom := range geoms {
|
for i, geom := range geoms {
|
||||||
if expected[i] != geom.Bounds() {
|
if expected[i] != geom.Bounds() {
|
||||||
t.Fatalf("%v != %v\n", expected[i], geom.Bounds())
|
t.Fatalf("%v != %v\n", expected[i], geom.Bounds())
|
||||||
|
@ -260,7 +263,7 @@ func TestFilterGeometryByType(t *testing.T) {
|
||||||
func TestClipper(t *testing.T) {
|
func TestClipper(t *testing.T) {
|
||||||
g := geos.NewGeos()
|
g := geos.NewGeos()
|
||||||
defer g.Finish()
|
defer g.Finish()
|
||||||
limiter, err := NewFromOgrSource("./hamburg_clip.geojson")
|
limiter, err := NewFromGeoJson("./hamburg_clip.geojson")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -302,7 +305,7 @@ func TestClipper(t *testing.T) {
|
||||||
func TestClipperWithBuffer(t *testing.T) {
|
func TestClipperWithBuffer(t *testing.T) {
|
||||||
g := geos.NewGeos()
|
g := geos.NewGeos()
|
||||||
defer g.Finish()
|
defer g.Finish()
|
||||||
limiter, err := NewFromOgrSourceWithBuffered("./hamburg_clip.geojson", 10000.0)
|
limiter, err := NewFromGeoJsonWithBuffered("./hamburg_clip.geojson", 10000.0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -315,44 +318,62 @@ func TestClipperWithBuffer(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSplitParams(t *testing.T) {
|
func TestSplitParams(t *testing.T) {
|
||||||
var gridWidth float64
|
var gridWidth, startWidth float64
|
||||||
|
|
||||||
gridWidth, _ = splitParams(geos.Bounds{0, 0, 10000, 10000}, 10, 2000)
|
gridWidth, startWidth = splitParams(geos.Bounds{0, 0, 10000, 10000}, 10, 2000)
|
||||||
if gridWidth != 2000.0 {
|
if gridWidth != 2000.0 {
|
||||||
t.Fatal(gridWidth)
|
t.Fatal(gridWidth)
|
||||||
}
|
}
|
||||||
|
if startWidth != 8000.0 {
|
||||||
|
t.Fatal(startWidth)
|
||||||
|
}
|
||||||
|
|
||||||
gridWidth, _ = splitParams(geos.Bounds{0, 0, 10000, 10000}, 10, 1000)
|
gridWidth, startWidth = splitParams(geos.Bounds{0, 0, 10000, 10000}, 10, 1000)
|
||||||
if gridWidth != 1000.0 {
|
if gridWidth != 1000.0 {
|
||||||
t.Fatal(gridWidth)
|
t.Fatal(gridWidth)
|
||||||
}
|
}
|
||||||
|
if startWidth != 8000.0 {
|
||||||
|
t.Fatal(startWidth)
|
||||||
|
}
|
||||||
|
|
||||||
gridWidth, _ = splitParams(geos.Bounds{0, 0, 10000, 10000}, 10, 500)
|
gridWidth, startWidth = splitParams(geos.Bounds{0, 0, 10000, 10000}, 10, 500)
|
||||||
if gridWidth != 1000.0 {
|
if gridWidth != 1000.0 {
|
||||||
t.Fatal(gridWidth)
|
t.Fatal(gridWidth)
|
||||||
}
|
}
|
||||||
|
if startWidth != 8000.0 {
|
||||||
|
t.Fatal(startWidth)
|
||||||
|
}
|
||||||
|
|
||||||
gridWidth, _ = splitParams(geos.Bounds{0, 0, 10000, 5000}, 10, 500)
|
gridWidth, startWidth = splitParams(geos.Bounds{0, 0, 10000, 5000}, 10, 500)
|
||||||
if gridWidth != 1000.0 {
|
if gridWidth != 1000.0 {
|
||||||
t.Fatal(gridWidth)
|
t.Fatal(gridWidth)
|
||||||
}
|
}
|
||||||
|
if startWidth != 8000.0 {
|
||||||
|
t.Fatal(startWidth)
|
||||||
|
}
|
||||||
|
|
||||||
gridWidth, _ = splitParams(geos.Bounds{0, 0, 10000, 20000}, 10, 500)
|
gridWidth, startWidth = splitParams(geos.Bounds{0, 0, 10000, 20000}, 10, 500)
|
||||||
if gridWidth != 2000.0 {
|
if gridWidth != 2000.0 {
|
||||||
t.Fatal(gridWidth)
|
t.Fatal(gridWidth)
|
||||||
}
|
}
|
||||||
|
if startWidth != 8000.0 {
|
||||||
|
t.Fatal(startWidth)
|
||||||
|
}
|
||||||
|
|
||||||
gridWidth, _ = splitParams(geos.Bounds{0, 0, 10000, 20000}, 50, 100)
|
gridWidth, startWidth = splitParams(geos.Bounds{0, 0, 10000, 20000}, 50, 100)
|
||||||
if gridWidth != 400.0 {
|
if gridWidth != 400.0 {
|
||||||
t.Fatal(gridWidth)
|
t.Fatal(gridWidth)
|
||||||
}
|
}
|
||||||
|
if startWidth != 6400.0 {
|
||||||
|
t.Fatal(startWidth)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkClipper(b *testing.B) {
|
func BenchmarkClipper(b *testing.B) {
|
||||||
g := geos.NewGeos()
|
g := geos.NewGeos()
|
||||||
defer g.Finish()
|
defer g.Finish()
|
||||||
limiter, err := NewFromOgrSource("./hamburg_clip.geojson")
|
limiter, err := NewFromGeoJson("./hamburg_clip.geojson")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
/*
|
|
||||||
Package ogr provides a wrapper to the GDAL/OGR library.
|
|
||||||
*/
|
|
||||||
package ogr
|
|
149
geom/ogr/ogr.go
149
geom/ogr/ogr.go
|
@ -1,149 +0,0 @@
|
||||||
package ogr
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo LDFLAGS: -lgdal
|
|
||||||
#include "ogr_api.h"
|
|
||||||
#include "cpl_error.h"
|
|
||||||
#include "cpl_conv.h"
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"imposm3/geom/geos"
|
|
||||||
"strings"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
C.OGRRegisterAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
type DataSource struct {
|
|
||||||
v C.OGRDataSourceH
|
|
||||||
}
|
|
||||||
|
|
||||||
type Layer struct {
|
|
||||||
v C.OGRLayerH
|
|
||||||
}
|
|
||||||
type OgrError struct {
|
|
||||||
message string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *OgrError) Error() string {
|
|
||||||
return e.message
|
|
||||||
}
|
|
||||||
|
|
||||||
func lastOgrError(fallback string) error {
|
|
||||||
msg := C.CPLGetLastErrorMsg()
|
|
||||||
if msg == nil {
|
|
||||||
return &OgrError{fallback}
|
|
||||||
}
|
|
||||||
str := C.GoString(msg)
|
|
||||||
if str == "" {
|
|
||||||
return &OgrError{fallback}
|
|
||||||
}
|
|
||||||
return &OgrError{str}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Open(name string) (*DataSource, error) {
|
|
||||||
namec := C.CString(name)
|
|
||||||
defer C.free(unsafe.Pointer(namec))
|
|
||||||
ds := C.OGROpen(namec, 0, nil)
|
|
||||||
if ds == nil {
|
|
||||||
return nil, lastOgrError("failed to open")
|
|
||||||
}
|
|
||||||
return &DataSource{ds}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ds *DataSource) Layer() (*Layer, error) {
|
|
||||||
layer := C.OGR_DS_GetLayer(ds.v, 0)
|
|
||||||
if layer == nil {
|
|
||||||
return nil, lastOgrError("failed to get layer 0")
|
|
||||||
}
|
|
||||||
return &Layer{layer}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ds *DataSource) Query(query string) (*Layer, error) {
|
|
||||||
// create select query if it is only a where statement
|
|
||||||
if !strings.HasPrefix(strings.ToLower(query), "select") {
|
|
||||||
layer, err := ds.Layer()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
layerDef := C.OGR_L_GetLayerDefn(layer.v)
|
|
||||||
name := C.OGR_FD_GetName(layerDef)
|
|
||||||
query = fmt.Sprintf("SELECT * FROM %s WHERE %s", C.GoString(name), query)
|
|
||||||
}
|
|
||||||
queryc := C.CString(query)
|
|
||||||
defer C.free(unsafe.Pointer(queryc))
|
|
||||||
layer := C.OGR_DS_ExecuteSQL(ds.v, queryc, nil, nil)
|
|
||||||
if layer == nil {
|
|
||||||
return nil, lastOgrError("unable to execute query '" + query + "'")
|
|
||||||
}
|
|
||||||
return &Layer{layer}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (layer *Layer) Wkts() chan string {
|
|
||||||
wkts := make(chan string)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer close(wkts)
|
|
||||||
|
|
||||||
C.OGR_L_ResetReading(layer.v)
|
|
||||||
for {
|
|
||||||
feature := C.OGR_L_GetNextFeature(layer.v)
|
|
||||||
if feature == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
geom := C.OGR_F_GetGeometryRef(feature)
|
|
||||||
var res *C.char
|
|
||||||
C.OGR_G_ExportToWkt(geom, &res)
|
|
||||||
wkts <- C.GoString(res)
|
|
||||||
C.CPLFree(unsafe.Pointer(res))
|
|
||||||
C.OGR_F_Destroy(feature)
|
|
||||||
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return wkts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (layer *Layer) Wkbs() chan []byte {
|
|
||||||
wkbs := make(chan []byte)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer close(wkbs)
|
|
||||||
|
|
||||||
C.OGR_L_ResetReading(layer.v)
|
|
||||||
for {
|
|
||||||
feature := C.OGR_L_GetNextFeature(layer.v)
|
|
||||||
if feature == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
geom := C.OGR_F_GetGeometryRef(feature)
|
|
||||||
size := C.OGR_G_WkbSize(geom)
|
|
||||||
buf := make([]byte, size)
|
|
||||||
C.OGR_G_ExportToWkb(geom, C.wkbNDR, (*C.uchar)(&buf[0]))
|
|
||||||
wkbs <- buf
|
|
||||||
C.OGR_F_Destroy(feature)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return wkbs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (layer *Layer) Geoms() chan *geos.Geom {
|
|
||||||
geoms := make(chan *geos.Geom)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer close(geoms)
|
|
||||||
g := geos.NewGeos()
|
|
||||||
defer g.Finish()
|
|
||||||
|
|
||||||
wkbs := layer.Wkbs()
|
|
||||||
for wkb := range wkbs {
|
|
||||||
geom := g.FromWkb(wkb)
|
|
||||||
geoms <- geom
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return geoms
|
|
||||||
}
|
|
|
@ -61,7 +61,7 @@ func main() {
|
||||||
if config.BaseOptions.LimitTo != "" {
|
if config.BaseOptions.LimitTo != "" {
|
||||||
var err error
|
var err error
|
||||||
step := log.StartStep("Reading limitto geometries")
|
step := log.StartStep("Reading limitto geometries")
|
||||||
geometryLimiter, err = limit.NewFromOgrSourceWithBuffered(
|
geometryLimiter, err = limit.NewFromGeoJsonWithBuffered(
|
||||||
config.BaseOptions.LimitTo,
|
config.BaseOptions.LimitTo,
|
||||||
config.BaseOptions.LimitToCacheBuffer,
|
config.BaseOptions.LimitToCacheBuffer,
|
||||||
)
|
)
|
||||||
|
@ -135,7 +135,7 @@ func mainimport() {
|
||||||
if config.ImportOptions.Write && config.BaseOptions.LimitTo != "" {
|
if config.ImportOptions.Write && config.BaseOptions.LimitTo != "" {
|
||||||
var err error
|
var err error
|
||||||
step := log.StartStep("Reading limitto geometries")
|
step := log.StartStep("Reading limitto geometries")
|
||||||
geometryLimiter, err = limit.NewFromOgrSource(config.BaseOptions.LimitTo)
|
geometryLimiter, err = limit.NewFromGeoJson(config.BaseOptions.LimitTo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue