mirror of https://github.com/tidwall/tile38.git
Add CLIP subcommand to INTERSECTS
This commit is contained in:
parent
d66abaccb0
commit
2be4eab627
|
@ -497,9 +497,13 @@ func (c *Collection) Within(sparse uint8, obj geojson.Object, minLat, minLon, ma
|
|||
}
|
||||
|
||||
// Intersects returns all object that are intersect an object or bounding box. Set obj to nil in order to use the bounding box.
|
||||
func (c *Collection) Intersects(sparse uint8, obj geojson.Object, minLat, minLon, maxLat, maxLon, lat, lon, meters, minZ, maxZ float64, iterator func(id string, obj geojson.Object, fields []float64) bool) bool {
|
||||
func (c *Collection) Intersects(
|
||||
sparse uint8, obj geojson.Object,
|
||||
minLat, minLon, maxLat, maxLon, lat, lon, meters, minZ, maxZ float64, doClip bool,
|
||||
iterator func(id string, obj geojson.Object, fields []float64, clipBox geojson.BBox) bool) bool {
|
||||
|
||||
var keepon = true
|
||||
var bbox geojson.BBox
|
||||
var clipbox, bbox geojson.BBox
|
||||
center := geojson.Position{X: lon, Y: lat, Z: 0}
|
||||
if obj != nil {
|
||||
bbox = obj.CalculatedBBox()
|
||||
|
@ -513,6 +517,9 @@ func (c *Collection) Intersects(sparse uint8, obj geojson.Object, minLat, minLon
|
|||
bbox = geojson.BBoxesFromCenter(lat, lon, meters)
|
||||
} else {
|
||||
bbox = geojson.BBox{Min: geojson.Position{X: minLon, Y: minLat, Z: minZ}, Max: geojson.Position{X: maxLon, Y: maxLat, Z: maxZ}}
|
||||
if doClip {
|
||||
clipbox = bbox
|
||||
}
|
||||
}
|
||||
var bboxes []geojson.BBox
|
||||
if sparse > 0 {
|
||||
|
@ -531,7 +538,7 @@ func (c *Collection) Intersects(sparse uint8, obj geojson.Object, minLat, minLon
|
|||
if obj != nil {
|
||||
keepon = c.geoSearch(bbox, func(id string, o geojson.Object, fields []float64) bool {
|
||||
if o.Intersects(obj) {
|
||||
if iterator(id, o, fields) {
|
||||
if iterator(id, o, fields, clipbox) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -540,7 +547,7 @@ func (c *Collection) Intersects(sparse uint8, obj geojson.Object, minLat, minLon
|
|||
} else if meters != -1 {
|
||||
keepon = c.geoSearch(bbox, func(id string, o geojson.Object, fields []float64) bool {
|
||||
if o.IntersectsCircle(center, meters) {
|
||||
if iterator(id, o, fields) {
|
||||
if iterator(id, o, fields, clipbox) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -550,7 +557,7 @@ func (c *Collection) Intersects(sparse uint8, obj geojson.Object, minLat, minLon
|
|||
if keepon {
|
||||
keepon = c.geoSearch(bbox, func(id string, o geojson.Object, fields []float64) bool {
|
||||
if o.IntersectsBBox(bbox) {
|
||||
if iterator(id, o, fields) {
|
||||
if iterator(id, o, fields, clipbox) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -566,21 +573,21 @@ func (c *Collection) Intersects(sparse uint8, obj geojson.Object, minLat, minLon
|
|||
if obj != nil {
|
||||
return c.geoSearch(bbox, func(id string, o geojson.Object, fields []float64) bool {
|
||||
if o.Intersects(obj) {
|
||||
return iterator(id, o, fields)
|
||||
return iterator(id, o, fields, clipbox)
|
||||
}
|
||||
return true
|
||||
})
|
||||
} else if meters != -1 {
|
||||
return c.geoSearch(bbox, func(id string, o geojson.Object, fields []float64) bool {
|
||||
if o.IntersectsCircle(center, meters) {
|
||||
return iterator(id, o, fields)
|
||||
return iterator(id, o, fields, clipbox)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
return c.geoSearch(bbox, func(id string, o geojson.Object, fields []float64) bool {
|
||||
if o.IntersectsBBox(bbox) {
|
||||
return iterator(id, o, fields)
|
||||
return iterator(id, o, fields, clipbox)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
|
|
@ -65,6 +65,8 @@ type ScanWriterParams struct {
|
|||
distance float64
|
||||
noLock bool
|
||||
ignoreGlobMatch bool
|
||||
clip bool
|
||||
clipbox geojson.BBox
|
||||
}
|
||||
|
||||
func (c *Controller) newScanWriter(
|
||||
|
@ -342,6 +344,9 @@ func (sw *scanWriter) writeObject(opts ScanWriterParams) bool {
|
|||
if sw.output == outputCount {
|
||||
return sw.count < sw.limit
|
||||
}
|
||||
if opts.clip {
|
||||
opts.o = opts.o.Clipped(opts.clipbox)
|
||||
}
|
||||
switch sw.msg.OutputType {
|
||||
case server.JSON:
|
||||
var wr bytes.Buffer
|
||||
|
|
|
@ -100,6 +100,10 @@ func (c *Controller) cmdSearchArgs(cmd string, vs []resp.Value, types []string)
|
|||
return
|
||||
}
|
||||
|
||||
if s.clip {
|
||||
err = errInvalidArgument("cannnot clip with point")
|
||||
}
|
||||
|
||||
umeters := true
|
||||
if vs, smeters, ok = tokenval(vs); !ok || smeters == "" {
|
||||
umeters = false
|
||||
|
@ -134,6 +138,10 @@ func (c *Controller) cmdSearchArgs(cmd string, vs []resp.Value, types []string)
|
|||
}
|
||||
}
|
||||
case "circle":
|
||||
if s.clip {
|
||||
err = errInvalidArgument("cannnot clip with circle")
|
||||
}
|
||||
|
||||
var slat, slon, smeters string
|
||||
if vs, slat, ok = tokenval(vs); !ok || slat == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
|
@ -165,6 +173,10 @@ func (c *Controller) cmdSearchArgs(cmd string, vs []resp.Value, types []string)
|
|||
return
|
||||
}
|
||||
case "object":
|
||||
if s.clip {
|
||||
err = errInvalidArgument("cannnot clip with object")
|
||||
}
|
||||
|
||||
var obj string
|
||||
if vs, obj, ok = tokenval(vs); !ok || obj == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
|
@ -258,6 +270,9 @@ func (c *Controller) cmdSearchArgs(cmd string, vs []resp.Value, types []string)
|
|||
}
|
||||
s.minLat, s.minLon, s.maxLat, s.maxLon = bing.TileXYToBounds(x, y, z)
|
||||
case "get":
|
||||
if s.clip {
|
||||
err = errInvalidArgument("cannnot clip with get")
|
||||
}
|
||||
var key, id string
|
||||
if vs, key, ok = tokenval(vs); !ok || key == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
|
@ -512,7 +527,8 @@ func (c *Controller) cmdWithinOrIntersects(cmd string, msg *server.Message) (res
|
|||
s.minLat, s.minLon, s.maxLat, s.maxLon,
|
||||
s.lat, s.lon, s.meters,
|
||||
minZ, maxZ,
|
||||
func(id string, o geojson.Object, fields []float64) bool {
|
||||
s.clip,
|
||||
func(id string, o geojson.Object, fields []float64, clipbox geojson.BBox) bool {
|
||||
if c.hasExpired(s.key, id) {
|
||||
return true
|
||||
}
|
||||
|
@ -521,6 +537,8 @@ func (c *Controller) cmdWithinOrIntersects(cmd string, msg *server.Message) (res
|
|||
o: o,
|
||||
fields: fields,
|
||||
noLock: true,
|
||||
clip: s.clip,
|
||||
clipbox: clipbox,
|
||||
})
|
||||
},
|
||||
)
|
||||
|
|
|
@ -245,6 +245,7 @@ type searchScanBaseTokens struct {
|
|||
usparse bool
|
||||
sparse uint8
|
||||
desc bool
|
||||
clip bool
|
||||
}
|
||||
|
||||
func (c *Controller) parseSearchScanBaseTokens(cmd string, vs []resp.Value) (vsout []resp.Value, t searchScanBaseTokens, err error) {
|
||||
|
@ -551,6 +552,14 @@ func (c *Controller) parseSearchScanBaseTokens(cmd string, vs []resp.Value) (vso
|
|||
return
|
||||
}
|
||||
continue
|
||||
} else if (wtok[0] == 'C' || wtok[0] == 'c') && strings.ToLower(wtok) == "clip" {
|
||||
vs = nvs
|
||||
if t.clip {
|
||||
err = errDuplicateArgument(strings.ToUpper(wtok))
|
||||
return
|
||||
}
|
||||
t.clip = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
|
|
|
@ -957,6 +957,12 @@
|
|||
"multiple": true,
|
||||
"variadic": true
|
||||
},
|
||||
{
|
||||
"command": "CLIP",
|
||||
"name": [],
|
||||
"type": [],
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"command": "NOFIELDS",
|
||||
"name": [],
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
package geojson
|
||||
|
||||
// Cohen-Sutherland Line Clipping
|
||||
// https://www.cs.helsinki.fi/group/goa/viewing/leikkaus/lineClip.html
|
||||
func ClipSegment(start, end Position, bbox BBox) (resStart, resEnd Position, rejected bool) {
|
||||
startCode := getCode(bbox, start)
|
||||
endCode := getCode(bbox, end)
|
||||
|
||||
if (startCode | endCode) == 0 {
|
||||
// trivially accept
|
||||
resStart, resEnd = start, end
|
||||
} else if (startCode & endCode) != 0 {
|
||||
// trivially reject
|
||||
rejected = true
|
||||
} else if startCode != 0 {
|
||||
// start is outside. get new start.
|
||||
newStart := intersect(bbox, startCode, start, end)
|
||||
resStart, resEnd, rejected = ClipSegment(newStart, end, bbox)
|
||||
} else {
|
||||
// end is outside. get new end.
|
||||
newEnd := intersect(bbox, endCode, start, end)
|
||||
resStart, resEnd, rejected = ClipSegment(start, newEnd, bbox)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Sutherland-Hodgman Polygon Clipping
|
||||
// https://www.cs.helsinki.fi/group/goa/viewing/leikkaus/intro2.html
|
||||
func ClipRing(ring[] Position, bbox BBox) (resRing []Position) {
|
||||
|
||||
if len(ring) < 4 {
|
||||
// under 4 elements this is not a polygon ring!
|
||||
return
|
||||
}
|
||||
|
||||
var edge uint8
|
||||
var inside, prevInside bool
|
||||
var prev Position
|
||||
|
||||
for edge = 1; edge <= 8; edge *=2 {
|
||||
prev = ring[len(ring) - 2]
|
||||
prevInside = (getCode(bbox, prev) & edge) == 0
|
||||
|
||||
for _, p := range ring {
|
||||
|
||||
inside = (getCode(bbox, p) & edge) == 0
|
||||
|
||||
if prevInside && inside {
|
||||
// Staying inside
|
||||
resRing = append(resRing, p)
|
||||
} else if prevInside && !inside {
|
||||
// Leaving
|
||||
resRing = append(resRing, intersect(bbox, edge, prev, p))
|
||||
} else if !prevInside && inside {
|
||||
// Entering
|
||||
resRing = append(resRing, intersect(bbox, edge, prev, p))
|
||||
resRing = append(resRing, p)
|
||||
} else {
|
||||
// Staying outside
|
||||
}
|
||||
|
||||
prev, prevInside = p, inside
|
||||
}
|
||||
|
||||
if resRing[0] != resRing[len(resRing)-1] {
|
||||
resRing = append(resRing, resRing[0])
|
||||
}
|
||||
ring, resRing = resRing, []Position{}
|
||||
}
|
||||
|
||||
resRing = ring
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func getCode(bbox BBox, point Position) (code uint8) {
|
||||
code = 0
|
||||
|
||||
if point.X < bbox.Min.X {
|
||||
code |= 1 // left
|
||||
} else if point.X > bbox.Max.X {
|
||||
code |= 2 // right
|
||||
}
|
||||
|
||||
if point.Y < bbox.Min.Y {
|
||||
code |= 4 // bottom
|
||||
} else if point.Y > bbox.Max.Y {
|
||||
code |= 8 // top
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func intersect(bbox BBox, code uint8, start, end Position) (new Position) {
|
||||
if (code & 8) != 0 { // top
|
||||
new = Position{
|
||||
X: start.X + (end.X - start.X) * (bbox.Max.Y - start.Y) / (end.Y - start.Y),
|
||||
Y: bbox.Max.Y,
|
||||
}
|
||||
} else if (code & 4) != 0 { // bottom
|
||||
new = Position{
|
||||
X: start.X + (end.X - start.X) * (bbox.Min.Y - start.Y) / (end.Y - start.Y),
|
||||
Y: bbox.Min.Y,
|
||||
}
|
||||
} else if (code & 2) != 0 { //right
|
||||
new = Position{
|
||||
X: bbox.Max.X,
|
||||
Y: start.Y + (end.Y - start.Y) * (bbox.Max.X - start.X) / (end.X - start.X),
|
||||
}
|
||||
} else if (code & 1) != 0 { // left
|
||||
new = Position{
|
||||
X: bbox.Min.X,
|
||||
Y: start.Y + (end.Y - start.Y) * (bbox.Min.X - start.X) / (end.X - start.X),
|
||||
}
|
||||
} else { // should not call intersect with the zero code
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package geojson
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestClipLineString(t *testing.T) {
|
||||
ls, _ := fillLineString(
|
||||
[]Position{
|
||||
{X: 1, Y: 1},
|
||||
{X: 2, Y: 2},
|
||||
{X: 3, Y: 1},
|
||||
}, nil, nil)
|
||||
bbox := BBox{
|
||||
Min: Position{X: 1.5, Y: 0.5},
|
||||
Max: Position{X: 2.5, Y: 1.8},
|
||||
}
|
||||
clipped := ls.Clipped(bbox)
|
||||
cl, ok := clipped.(MultiLineString)
|
||||
if !ok {
|
||||
t.Fatal("wrong type")
|
||||
}
|
||||
if len(cl.Coordinates) != 2 {
|
||||
t.Fatal("result must have two parts in MultiString")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func TestClipPolygon(t *testing.T) {
|
||||
outer := []Position{
|
||||
{X: 2, Y: 2},
|
||||
{X: 1, Y: 2},
|
||||
{X: 1.5, Y: 1.5},
|
||||
{X: 1, Y: 1},
|
||||
{X: 2, Y: 1},
|
||||
{X: 2, Y: 2},
|
||||
}
|
||||
inner := []Position{
|
||||
{X: 1.9, Y: 1.9},
|
||||
{X: 1.2, Y: 1.9},
|
||||
{X: 1.45, Y: 1.65},
|
||||
{X: 1.9, Y: 1.5},
|
||||
{X: 1.9, Y: 1.9},
|
||||
|
||||
}
|
||||
polygon, _ := fillPolygon([][]Position{outer, inner}, nil, nil)
|
||||
bbox := BBox{
|
||||
Min: Position{X: 1.3, Y: 1.3},
|
||||
Max: Position{X: 1.4, Y: 2.15},
|
||||
}
|
||||
clipped := polygon.Clipped(bbox)
|
||||
cp, ok := clipped.(Polygon)
|
||||
if !ok {
|
||||
t.Fatal("wrong type")
|
||||
}
|
||||
if len(cp.Coordinates) != 2 {
|
||||
t.Fatal("result must have two parts in Polygon")
|
||||
}
|
||||
}
|
|
@ -232,3 +232,14 @@ func (g Feature) IsBBoxDefined() bool {
|
|||
func (g Feature) IsGeometry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Clip returns the object obtained by clipping this object by a bbox.
|
||||
func (g Feature) Clipped(bbox BBox) Object {
|
||||
clippedGeometry := g.Geometry.Clipped(bbox)
|
||||
|
||||
res := Feature{Geometry: clippedGeometry, idprops: g.idprops}
|
||||
cbbox := clippedGeometry.CalculatedBBox()
|
||||
res.BBox = &cbbox
|
||||
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -248,3 +248,16 @@ func (g FeatureCollection) IsBBoxDefined() bool {
|
|||
func (g FeatureCollection) IsGeometry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Clip returns the object obtained by clipping this object by a bbox.
|
||||
func (g FeatureCollection) Clipped(bbox BBox) Object {
|
||||
var new_features []Object
|
||||
for _, feature := range g.Features {
|
||||
new_features = append(new_features, feature.Clipped(bbox))
|
||||
}
|
||||
|
||||
fc := FeatureCollection{Features: new_features}
|
||||
cbbox := fc.CalculatedBBox()
|
||||
fc.BBox = &cbbox
|
||||
return fc
|
||||
}
|
||||
|
|
|
@ -246,3 +246,16 @@ func (g GeometryCollection) IsBBoxDefined() bool {
|
|||
func (g GeometryCollection) IsGeometry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Clip returns the object of the same type as this object, clipped by a bbox.
|
||||
func (g GeometryCollection) Clipped(bbox BBox) Object {
|
||||
var new_geometries []Object
|
||||
for _, geometry := range g.Geometries {
|
||||
new_geometries = append(new_geometries, geometry.Clipped(bbox))
|
||||
}
|
||||
|
||||
gc := GeometryCollection{Geometries: new_geometries}
|
||||
cbbox := gc.CalculatedBBox()
|
||||
gc.BBox = &cbbox
|
||||
return gc
|
||||
}
|
||||
|
|
|
@ -151,3 +151,31 @@ func (g LineString) IsBBoxDefined() bool {
|
|||
func (g LineString) IsGeometry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Clip returns the object obtained by clipping this object by a bbox.
|
||||
func (g LineString) Clipped(bbox BBox) Object {
|
||||
var new_coordinates [][]Position
|
||||
var clipedStart, clippedEnd Position
|
||||
var rejected bool
|
||||
var line []Position
|
||||
|
||||
for i := 0; i < len(g.Coordinates) - 1 ; i++ {
|
||||
clipedStart, clippedEnd, rejected = ClipSegment(g.Coordinates[i], g.Coordinates[i + 1], bbox)
|
||||
if rejected {
|
||||
continue
|
||||
}
|
||||
if len(line) > 0 && line[len(line) - 1] != clipedStart {
|
||||
new_coordinates = append(new_coordinates, line)
|
||||
line = []Position{clipedStart}
|
||||
} else if len(line) == 0 {
|
||||
line = append(line, clipedStart)
|
||||
}
|
||||
line = append(line, clippedEnd)
|
||||
}
|
||||
if len(line) > 0 {
|
||||
new_coordinates = append(new_coordinates, line)
|
||||
}
|
||||
|
||||
res, _ := fillMultiLineString(new_coordinates, nil, nil)
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -209,3 +209,18 @@ func (g MultiLineString) IsBBoxDefined() bool {
|
|||
func (g MultiLineString) IsGeometry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Clip returns the object obtained by clipping this object by a bbox.
|
||||
func (g MultiLineString) Clipped(bbox BBox) Object {
|
||||
var new_coordinates [][]Position
|
||||
|
||||
for ix := range g.Coordinates {
|
||||
clippedMultiLineString, _ := g.getLineString(ix).Clipped(bbox).(MultiLineString)
|
||||
for _, ls := range clippedMultiLineString.Coordinates {
|
||||
new_coordinates = append(new_coordinates, ls)
|
||||
}
|
||||
}
|
||||
|
||||
res, _ := fillMultiLineString(new_coordinates, nil, nil)
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -177,3 +177,18 @@ func (g MultiPoint) IsBBoxDefined() bool {
|
|||
func (g MultiPoint) IsGeometry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Clip returns the object obtained by clipping this object by a bbox.
|
||||
func (g MultiPoint) Clipped(bbox BBox) Object {
|
||||
var new_coordinates []Position
|
||||
|
||||
for _, position := range g.Coordinates {
|
||||
if poly.Point(position).InsideRect(rectBBox(bbox)) {
|
||||
new_coordinates = append(new_coordinates, position)
|
||||
}
|
||||
}
|
||||
|
||||
res, _ := fillMultiPoint(new_coordinates, nil, nil)
|
||||
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -215,3 +215,18 @@ func (g MultiPolygon) IsBBoxDefined() bool {
|
|||
func (g MultiPolygon) IsGeometry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Clip returns the object obtained by clipping this object by a bbox.
|
||||
func (g MultiPolygon) Clipped(bbox BBox) Object {
|
||||
var new_coordinates [][][]Position
|
||||
|
||||
for _, polygon := range g.polygons {
|
||||
clippedMultiPolygon, _ := polygon.Clipped(bbox).(MultiPolygon)
|
||||
for _, pg := range clippedMultiPolygon.Coordinates {
|
||||
new_coordinates = append(new_coordinates, pg)
|
||||
}
|
||||
}
|
||||
|
||||
res, _ := fillMultiPolygon(new_coordinates, nil, nil)
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -94,6 +94,8 @@ type Object interface {
|
|||
IsBBoxDefined() bool
|
||||
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
|
||||
IsGeometry() bool
|
||||
// Clip returns the object obtained by clipping this object by a bbox.
|
||||
Clipped(bbox BBox) Object
|
||||
}
|
||||
|
||||
func positionBBox(i int, bbox BBox, ps []Position) (int, BBox) {
|
||||
|
|
|
@ -145,3 +145,13 @@ func (g Point) IsBBoxDefined() bool {
|
|||
func (g Point) IsGeometry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Clip returns the object obtained by clipping this object by a bbox.
|
||||
func (g Point) Clipped(bbox BBox) Object {
|
||||
if g.IntersectsBBox(bbox) {
|
||||
return g
|
||||
}
|
||||
|
||||
res, _ := fillMultiPoint([]Position{}, nil, nil)
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -209,3 +209,15 @@ func (g Polygon) IsBBoxDefined() bool {
|
|||
func (g Polygon) IsGeometry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Clip returns the object obtained by clipping this object by a bbox.
|
||||
func (g Polygon) Clipped(bbox BBox) Object {
|
||||
var new_coordinates [][]Position
|
||||
|
||||
for _, ring := range g.Coordinates {
|
||||
new_coordinates = append(new_coordinates, ClipRing(ring, bbox))
|
||||
}
|
||||
|
||||
res, _ := fillPolygon(new_coordinates, nil, nil)
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -127,3 +127,12 @@ func (g SimplePoint) IsBBoxDefined() bool {
|
|||
func (g SimplePoint) IsGeometry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Clip returns the object obtained by clipping this object by a bbox.
|
||||
func (g SimplePoint) Clipped(bbox BBox) Object {
|
||||
if g.IntersectsBBox(bbox) {
|
||||
return g
|
||||
}
|
||||
res, _ := fillMultiPoint([]Position{}, nil, nil)
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -73,6 +73,11 @@ func (s String) IsGeometry() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Clip returns the object obtained by clipping this object by a bbox.
|
||||
func (s String) Clipped(bbox BBox) Object {
|
||||
return s
|
||||
}
|
||||
|
||||
// Bytes is the bytes representation of the object.
|
||||
func (s String) Bytes() []byte {
|
||||
return []byte(s.String())
|
||||
|
|
Loading…
Reference in New Issue