improve limiter
- check complete contains first - intersect with original geometry if it intersects to many partsmaster
parent
60a4eb7f4d
commit
5203225bcd
|
@ -248,6 +248,5 @@ func geosPolygon(g *geos.Geos, polygon polygon) (*geos.Geom, error) {
|
||||||
}
|
}
|
||||||
return nil, errors.New("unable to create polygon")
|
return nil, errors.New("unable to create polygon")
|
||||||
}
|
}
|
||||||
g.DestroyLater(geom)
|
|
||||||
return geom, nil
|
return geom, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,10 +107,23 @@ func SplitPolygonAtGrid(g *geos.Geos, geom *geos.Geom, gridWidth, currentGridWid
|
||||||
}
|
}
|
||||||
|
|
||||||
type Limiter struct {
|
type Limiter struct {
|
||||||
index *geos.Index
|
// for quick intersections of small geometries
|
||||||
|
index *geos.Index
|
||||||
|
// for direct intersections of large geometries
|
||||||
|
geom *geos.Geom
|
||||||
|
// for quick contains checks
|
||||||
|
geomPrep *geos.PreparedGeom
|
||||||
|
geomPrepMu *sync.Mutex
|
||||||
|
|
||||||
|
// bufferedXxx for diff elements
|
||||||
|
// (we keep more data at the boundary to be able to build
|
||||||
|
// geometries that intersects the limitto geometry)
|
||||||
|
|
||||||
|
// for quick coarse contains checks
|
||||||
|
bufferedBbox geos.Bounds
|
||||||
|
// for quick contains checks
|
||||||
bufferedPrep *geos.PreparedGeom
|
bufferedPrep *geos.PreparedGeom
|
||||||
bufferedPrepMu *sync.Mutex
|
bufferedPrepMu *sync.Mutex
|
||||||
bufferedBbox geos.Bounds
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFromGeoJson(source string) (*Limiter, error) {
|
func NewFromGeoJson(source string) (*Limiter, error) {
|
||||||
|
@ -138,6 +151,7 @@ func NewFromGeoJsonWithBuffered(source string, buffer float64) (*Limiter, error)
|
||||||
|
|
||||||
index := g.CreateIndex()
|
index := g.CreateIndex()
|
||||||
|
|
||||||
|
var bufferedPolygons []*geos.Geom
|
||||||
var polygons []*geos.Geom
|
var polygons []*geos.Geom
|
||||||
|
|
||||||
withBuffer := false
|
withBuffer := false
|
||||||
|
@ -157,8 +171,9 @@ func NewFromGeoJsonWithBuffered(source string, buffer float64) (*Limiter, error)
|
||||||
return nil, errors.New("couldn't buffer limitto")
|
return nil, errors.New("couldn't buffer limitto")
|
||||||
}
|
}
|
||||||
g.DestroyLater(buffered)
|
g.DestroyLater(buffered)
|
||||||
polygons = append(polygons, buffered)
|
bufferedPolygons = append(bufferedPolygons, buffered)
|
||||||
}
|
}
|
||||||
|
polygons = append(polygons, geom)
|
||||||
|
|
||||||
parts, err := SplitPolygonAtAutoGrid(g, geom)
|
parts, err := SplitPolygonAtAutoGrid(g, geom)
|
||||||
|
|
||||||
|
@ -170,10 +185,10 @@ func NewFromGeoJsonWithBuffered(source string, buffer float64) (*Limiter, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var bbox geos.Bounds
|
var bufferedBbox geos.Bounds
|
||||||
var prep *geos.PreparedGeom
|
var bufferedPrep *geos.PreparedGeom
|
||||||
if len(polygons) > 0 {
|
if len(bufferedPolygons) > 0 {
|
||||||
union := g.UnionPolygons(polygons)
|
union := g.UnionPolygons(bufferedPolygons)
|
||||||
if union == nil {
|
if union == nil {
|
||||||
return nil, errors.New("unable to union limitto polygons")
|
return nil, errors.New("unable to union limitto polygons")
|
||||||
}
|
}
|
||||||
|
@ -182,14 +197,24 @@ func NewFromGeoJsonWithBuffered(source string, buffer float64) (*Limiter, error)
|
||||||
return nil, errors.New("unable to simplify limitto polygons")
|
return nil, errors.New("unable to simplify limitto polygons")
|
||||||
}
|
}
|
||||||
g.Destroy(union)
|
g.Destroy(union)
|
||||||
bbox = simplified.Bounds()
|
bufferedBbox = simplified.Bounds()
|
||||||
prep = g.Prepare(simplified)
|
bufferedPrep = g.Prepare(simplified)
|
||||||
// keep simplified around for prepared geometry
|
// keep simplified around for prepared geometry
|
||||||
if prep == nil {
|
if bufferedPrep == nil {
|
||||||
return nil, errors.New("unable to prepare limitto polygons")
|
return nil, errors.New("unable to prepare limitto polygons")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &Limiter{index, prep, &sync.Mutex{}, bbox}, nil
|
var geomPrep *geos.PreparedGeom
|
||||||
|
union := g.UnionPolygons(polygons)
|
||||||
|
if union == nil {
|
||||||
|
return nil, errors.New("unable to union limitto polygons")
|
||||||
|
}
|
||||||
|
geomPrep = g.Prepare(union)
|
||||||
|
if geomPrep == nil {
|
||||||
|
return nil, errors.New("unable to prepare limitto polygons")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Limiter{index, union, geomPrep, &sync.Mutex{}, bufferedBbox, bufferedPrep, &sync.Mutex{}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterGeometryByType(g *geos.Geos, geom *geos.Geom, targetType string) []*geos.Geom {
|
func filterGeometryByType(g *geos.Geos, geom *geos.Geom, targetType string) []*geos.Geom {
|
||||||
|
@ -234,22 +259,34 @@ func (l *Limiter) Clip(geom *geos.Geom) ([]*geos.Geom, error) {
|
||||||
g := geos.NewGeos()
|
g := geos.NewGeos()
|
||||||
defer g.Finish()
|
defer g.Finish()
|
||||||
|
|
||||||
|
// check if geom is completely contained
|
||||||
|
l.geomPrepMu.Lock()
|
||||||
|
if g.PreparedContains(l.geomPrep, geom) {
|
||||||
|
l.geomPrepMu.Unlock()
|
||||||
|
return []*geos.Geom{geom}, nil
|
||||||
|
}
|
||||||
|
l.geomPrepMu.Unlock()
|
||||||
|
|
||||||
|
// we have intersections, query index to get intersecting parts
|
||||||
hits := g.IndexQuery(l.index, geom)
|
hits := g.IndexQuery(l.index, geom)
|
||||||
|
|
||||||
if len(hits) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
geomType := g.Type(geom)
|
geomType := g.Type(geom)
|
||||||
|
|
||||||
var intersections []*geos.Geom
|
// too many intersecting parts, it probably faster to
|
||||||
|
// intersect with the original geometry
|
||||||
|
if len(hits) > 25 {
|
||||||
|
newPart := g.Intersection(l.geom, geom)
|
||||||
|
if newPart == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
newParts := filterGeometryByType(g, newPart, geomType)
|
||||||
|
return mergeGeometries(g, newParts, geomType), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var intersections []*geos.Geom
|
||||||
|
// intersect with each part...
|
||||||
for _, hit := range hits {
|
for _, hit := range hits {
|
||||||
hit.Lock()
|
hit.Lock()
|
||||||
if g.PreparedContains(hit.Prepared, geom) {
|
|
||||||
hit.Unlock()
|
|
||||||
return []*geos.Geom{geom}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if g.PreparedIntersects(hit.Prepared, geom) {
|
if g.PreparedIntersects(hit.Prepared, geom) {
|
||||||
hit.Unlock()
|
hit.Unlock()
|
||||||
newPart := g.Intersection(hit.Geom, geom)
|
newPart := g.Intersection(hit.Geom, geom)
|
||||||
|
@ -264,6 +301,7 @@ func (l *Limiter) Clip(geom *geos.Geom) ([]*geos.Geom, error) {
|
||||||
hit.Unlock()
|
hit.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// and merge parts back to our clipped intersection
|
||||||
return mergeGeometries(g, intersections, geomType), nil
|
return mergeGeometries(g, intersections, geomType), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue