mirror of https://github.com/tidwall/tile38.git
Precalculate and store bboxes for complex objects
This should increase the performance for most search operations such as WITHIN, INTERSECTS, and NEARBY when dealing with complex polygons. Geofencing should see a increase in throughput when dealing with a high frequency of point -> polygon detection. Addresses #245
This commit is contained in:
parent
44a6acf107
commit
1f70cb4e75
|
@ -137,17 +137,6 @@ func (c *Controller) aofshrink() {
|
|||
values = append(values, "point")
|
||||
values = append(values, strconv.FormatFloat(obj.Y, 'f', -1, 64))
|
||||
values = append(values, strconv.FormatFloat(obj.X, 'f', -1, 64))
|
||||
case geojson.Point:
|
||||
if obj.Coordinates.Z == 0 {
|
||||
values = append(values, "point")
|
||||
values = append(values, strconv.FormatFloat(obj.Coordinates.Y, 'f', -1, 64))
|
||||
values = append(values, strconv.FormatFloat(obj.Coordinates.X, 'f', -1, 64))
|
||||
values = append(values, strconv.FormatFloat(obj.Coordinates.Z, 'f', -1, 64))
|
||||
} else {
|
||||
values = append(values, "point")
|
||||
values = append(values, strconv.FormatFloat(obj.Coordinates.Y, 'f', -1, 64))
|
||||
values = append(values, strconv.FormatFloat(obj.Coordinates.X, 'f', -1, 64))
|
||||
}
|
||||
}
|
||||
|
||||
// append the values to the aof buffer
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package geojson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
|
@ -63,28 +62,29 @@ func (b *BBox) isCordZDefined() bool {
|
|||
return b != nil && (b.Min.Z != nilz || b.Max.Z != nilz)
|
||||
}
|
||||
|
||||
func (b *BBox) write(buf *bytes.Buffer) {
|
||||
func appendBBoxJSON(json []byte, b *BBox) []byte {
|
||||
if b == nil {
|
||||
return
|
||||
return json
|
||||
}
|
||||
hasZ := b.Min.Z != nilz && b.Max.Z != nilz
|
||||
buf.WriteString(`,"bbox":[`)
|
||||
buf.WriteString(strconv.FormatFloat(b.Min.X, 'f', -1, 64))
|
||||
buf.WriteByte(',')
|
||||
buf.WriteString(strconv.FormatFloat(b.Min.Y, 'f', -1, 64))
|
||||
json = append(json, `,"bbox":[`...)
|
||||
json = strconv.AppendFloat(json, b.Min.X, 'f', -1, 64)
|
||||
json = append(json, ',')
|
||||
json = strconv.AppendFloat(json, b.Min.Y, 'f', -1, 64)
|
||||
if hasZ {
|
||||
buf.WriteByte(',')
|
||||
buf.WriteString(strconv.FormatFloat(b.Min.Z, 'f', -1, 64))
|
||||
json = append(json, ',')
|
||||
json = strconv.AppendFloat(json, b.Min.Z, 'f', -1, 64)
|
||||
}
|
||||
buf.WriteByte(',')
|
||||
buf.WriteString(strconv.FormatFloat(b.Max.X, 'f', -1, 64))
|
||||
buf.WriteByte(',')
|
||||
buf.WriteString(strconv.FormatFloat(b.Max.Y, 'f', -1, 64))
|
||||
json = append(json, ',')
|
||||
json = strconv.AppendFloat(json, b.Max.X, 'f', -1, 64)
|
||||
json = append(json, ',')
|
||||
json = strconv.AppendFloat(json, b.Max.Y, 'f', -1, 64)
|
||||
if hasZ {
|
||||
buf.WriteByte(',')
|
||||
buf.WriteString(strconv.FormatFloat(b.Max.Z, 'f', -1, 64))
|
||||
json = append(json, ',')
|
||||
json = strconv.AppendFloat(json, b.Max.Z, 'f', -1, 64)
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
json = append(json, ']')
|
||||
return json
|
||||
}
|
||||
|
||||
func (b BBox) center() Position {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package geojson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
|
@ -10,9 +9,10 @@ import (
|
|||
|
||||
// Feature is a geojson object with the type "Feature"
|
||||
type Feature struct {
|
||||
Geometry Object
|
||||
BBox *BBox
|
||||
idprops string // raw id and properties seperated by a '\0'
|
||||
Geometry Object
|
||||
BBox *BBox
|
||||
bboxDefined bool
|
||||
idprops string // raw id and properties seperated by a '\0'
|
||||
}
|
||||
|
||||
func fillFeatureMap(json string) (Feature, error) {
|
||||
|
@ -35,7 +35,11 @@ func fillFeatureMap(json string) (Feature, error) {
|
|||
if err != nil {
|
||||
return g, err
|
||||
}
|
||||
|
||||
g.bboxDefined = g.BBox != nil
|
||||
if !g.bboxDefined {
|
||||
cbbox := g.CalculatedBBox()
|
||||
g.BBox = &cbbox
|
||||
}
|
||||
var propsExists bool
|
||||
props := gjson.Get(json, "properties")
|
||||
switch props.Type {
|
||||
|
@ -89,7 +93,7 @@ func (g Feature) Weight() int {
|
|||
|
||||
// MarshalJSON allows the object to be encoded in json.Marshal calls.
|
||||
func (g Feature) MarshalJSON() ([]byte, error) {
|
||||
return []byte(g.JSON()), nil
|
||||
return g.appendJSON(nil), nil
|
||||
}
|
||||
|
||||
func (g Feature) getRaw() (id, props string) {
|
||||
|
@ -128,23 +132,27 @@ func makeCompositeRaw(idRaw, propsRaw string) string {
|
|||
return string(raw)
|
||||
}
|
||||
|
||||
// JSON is the json representation of the object. This might not be exactly the same as the original.
|
||||
func (g Feature) JSON() string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(`{"type":"Feature","geometry":`)
|
||||
buf.WriteString(g.Geometry.JSON())
|
||||
g.BBox.write(&buf)
|
||||
func (g Feature) appendJSON(json []byte) []byte {
|
||||
json = append(json, `{"type":"Feature","geometry":`...)
|
||||
json = append(json, g.Geometry.JSON()...)
|
||||
if g.bboxDefined {
|
||||
json = appendBBoxJSON(json, g.BBox)
|
||||
}
|
||||
idRaw, propsRaw := g.getRaw()
|
||||
if propsRaw != "" {
|
||||
buf.WriteString(`,"properties":`)
|
||||
buf.WriteString(propsRaw)
|
||||
json = append(json, `,"properties":`...)
|
||||
json = append(json, propsRaw...)
|
||||
}
|
||||
if idRaw != "" {
|
||||
buf.WriteString(`,"id":`)
|
||||
buf.WriteString(idRaw)
|
||||
json = append(json, `,"id":`...)
|
||||
json = append(json, idRaw...)
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
return buf.String()
|
||||
return append(json, '}')
|
||||
}
|
||||
|
||||
// JSON is the json representation of the object. This might not be exactly the same as the original.
|
||||
func (g Feature) JSON() string {
|
||||
return string(g.appendJSON(nil))
|
||||
}
|
||||
|
||||
// String returns a string representation of the object. This might be JSON or something else.
|
||||
|
@ -160,7 +168,7 @@ func (g Feature) bboxPtr() *BBox {
|
|||
return g.BBox
|
||||
}
|
||||
func (g Feature) hasPositions() bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return true
|
||||
}
|
||||
return g.Geometry.hasPositions()
|
||||
|
@ -168,7 +176,7 @@ func (g Feature) hasPositions() bool {
|
|||
|
||||
// WithinBBox detects if the object is fully contained inside a bbox.
|
||||
func (g Feature) WithinBBox(bbox BBox) bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox))
|
||||
}
|
||||
return g.Geometry.WithinBBox(bbox)
|
||||
|
@ -176,7 +184,7 @@ func (g Feature) WithinBBox(bbox BBox) bool {
|
|||
|
||||
// IntersectsBBox detects if the object intersects a bbox.
|
||||
func (g Feature) IntersectsBBox(bbox BBox) bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox))
|
||||
}
|
||||
return g.Geometry.IntersectsBBox(bbox)
|
||||
|
@ -213,7 +221,7 @@ func (g Feature) Nearby(center Position, meters float64) bool {
|
|||
|
||||
// IsBBoxDefined returns true if the object has a defined bbox.
|
||||
func (g Feature) IsBBoxDefined() bool {
|
||||
return g.BBox != nil
|
||||
return g.bboxDefined
|
||||
}
|
||||
|
||||
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package geojson
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFeature(t *testing.T) {
|
||||
testJSON(t, `{
|
||||
|
@ -116,3 +118,652 @@ func TestComplexFeature(t *testing.T) {
|
|||
}
|
||||
o = o
|
||||
}
|
||||
|
||||
func TestIssue245(t *testing.T) {
|
||||
json := `{
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[113.840391, 23.36073],
|
||||
[113.840837, 23.360618],
|
||||
[113.841198, 23.360528],
|
||||
[113.84295, 23.360252],
|
||||
[113.844386, 23.359914],
|
||||
[113.844674, 23.359847],
|
||||
[113.845843, 23.359466],
|
||||
[113.846787, 23.358753],
|
||||
[113.84679, 23.358751],
|
||||
[113.847128, 23.358254],
|
||||
[113.847571, 23.357602],
|
||||
[113.847573, 23.357598],
|
||||
[113.848175, 23.356521],
|
||||
[113.848177, 23.356517],
|
||||
[113.848187, 23.3565],
|
||||
[113.848189, 23.356497],
|
||||
[113.84847, 23.355449],
|
||||
[113.848471, 23.355445],
|
||||
[113.848475, 23.355126],
|
||||
[113.848486, 23.354203],
|
||||
[113.848373, 23.353516],
|
||||
[113.848122, 23.352467],
|
||||
[113.847432, 23.351258],
|
||||
[113.846102, 23.349542],
|
||||
[113.845853, 23.349288],
|
||||
[113.84494, 23.348358],
|
||||
[113.844435, 23.347908],
|
||||
[113.843638, 23.347198],
|
||||
[113.840895, 23.345392],
|
||||
[113.836433, 23.342373],
|
||||
[113.835963, 23.342033],
|
||||
[113.83469, 23.34111],
|
||||
[113.833801, 23.340465],
|
||||
[113.831238, 23.338917],
|
||||
[113.830698, 23.33835],
|
||||
[113.830644, 23.337639],
|
||||
[113.830642, 23.337639],
|
||||
[113.825764, 23.337862],
|
||||
[113.82575, 23.337897],
|
||||
[113.82575, 23.337899],
|
||||
[113.825749, 23.337901],
|
||||
[113.825748, 23.337905],
|
||||
[113.825747, 23.337907],
|
||||
[113.825746, 23.337909],
|
||||
[113.825746, 23.337912],
|
||||
[113.825745, 23.337914],
|
||||
[113.825744, 23.337916],
|
||||
[113.825743, 23.337919],
|
||||
[113.825742, 23.337921],
|
||||
[113.825741, 23.337923],
|
||||
[113.82574, 23.337926],
|
||||
[113.825739, 23.337928],
|
||||
[113.825738, 23.33793],
|
||||
[113.825738, 23.337933],
|
||||
[113.825737, 23.337935],
|
||||
[113.825736, 23.337937],
|
||||
[113.825735, 23.33794],
|
||||
[113.825734, 23.337942],
|
||||
[113.825733, 23.337944],
|
||||
[113.825732, 23.337947],
|
||||
[113.825731, 23.337949],
|
||||
[113.82573, 23.337951],
|
||||
[113.825729, 23.337954],
|
||||
[113.825728, 23.337956],
|
||||
[113.825727, 23.337959],
|
||||
[113.825726, 23.337962],
|
||||
[113.825725, 23.337964],
|
||||
[113.825725, 23.337966],
|
||||
[113.825724, 23.337969],
|
||||
[113.825723, 23.337971],
|
||||
[113.825722, 23.337973],
|
||||
[113.825721, 23.337975],
|
||||
[113.82572, 23.337978],
|
||||
[113.825719, 23.33798],
|
||||
[113.825718, 23.337982],
|
||||
[113.825717, 23.337985],
|
||||
[113.825717, 23.337987],
|
||||
[113.825716, 23.337989],
|
||||
[113.825715, 23.337992],
|
||||
[113.825715, 23.337994],
|
||||
[113.825714, 23.337996],
|
||||
[113.825713, 23.337999],
|
||||
[113.825712, 23.338001],
|
||||
[113.825711, 23.338003],
|
||||
[113.82571, 23.338006],
|
||||
[113.825709, 23.338008],
|
||||
[113.825708, 23.33801],
|
||||
[113.825707, 23.338013],
|
||||
[113.825706, 23.338015],
|
||||
[113.825705, 23.338018],
|
||||
[113.825705, 23.338021],
|
||||
[113.825704, 23.338023],
|
||||
[113.825703, 23.338025],
|
||||
[113.825702, 23.338028],
|
||||
[113.825701, 23.33803],
|
||||
[113.8257, 23.338032],
|
||||
[113.825699, 23.338035],
|
||||
[113.825698, 23.338037],
|
||||
[113.825697, 23.338039],
|
||||
[113.825696, 23.338042],
|
||||
[113.825696, 23.338044],
|
||||
[113.825695, 23.338046],
|
||||
[113.825694, 23.338049],
|
||||
[113.825693, 23.338051],
|
||||
[113.825692, 23.338053],
|
||||
[113.825691, 23.338056],
|
||||
[113.82569, 23.338058],
|
||||
[113.82569, 23.338061],
|
||||
[113.825689, 23.338063],
|
||||
[113.825689, 23.338065],
|
||||
[113.825688, 23.338068],
|
||||
[113.825687, 23.33807],
|
||||
[113.825686, 23.338072],
|
||||
[113.825685, 23.338075],
|
||||
[113.825684, 23.338078],
|
||||
[113.825683, 23.33808],
|
||||
[113.825682, 23.338083],
|
||||
[113.825682, 23.338085],
|
||||
[113.825681, 23.338087],
|
||||
[113.82568, 23.33809],
|
||||
[113.825679, 23.338092],
|
||||
[113.825678, 23.338094],
|
||||
[113.825677, 23.338097],
|
||||
[113.825676, 23.338099],
|
||||
[113.825675, 23.338101],
|
||||
[113.825675, 23.338104],
|
||||
[113.825674, 23.338106],
|
||||
[113.825673, 23.338108],
|
||||
[113.825672, 23.338111],
|
||||
[113.825671, 23.338113],
|
||||
[113.82567, 23.338115],
|
||||
[113.825669, 23.338118],
|
||||
[113.825669, 23.33812],
|
||||
[113.825668, 23.338123],
|
||||
[113.825668, 23.338125],
|
||||
[113.825667, 23.338127],
|
||||
[113.825666, 23.33813],
|
||||
[113.825665, 23.338132],
|
||||
[113.825664, 23.338134],
|
||||
[113.825664, 23.338138],
|
||||
[113.825663, 23.33814],
|
||||
[113.825662, 23.338142],
|
||||
[113.825661, 23.338145],
|
||||
[113.82566, 23.338147],
|
||||
[113.825659, 23.338149],
|
||||
[113.825659, 23.338152],
|
||||
[113.825658, 23.338154],
|
||||
[113.825657, 23.338156],
|
||||
[113.825656, 23.338159],
|
||||
[113.825655, 23.338161],
|
||||
[113.825654, 23.338164],
|
||||
[113.825654, 23.338166],
|
||||
[113.825653, 23.338168],
|
||||
[113.825652, 23.338171],
|
||||
[113.825651, 23.338173],
|
||||
[113.82565, 23.338175],
|
||||
[113.82565, 23.338178],
|
||||
[113.825649, 23.33818],
|
||||
[113.825648, 23.338182],
|
||||
[113.825647, 23.338185],
|
||||
[113.825646, 23.338187],
|
||||
[113.825646, 23.33819],
|
||||
[113.825646, 23.338192],
|
||||
[113.825645, 23.338194],
|
||||
[113.825644, 23.338197],
|
||||
[113.825643, 23.3382],
|
||||
[113.825642, 23.338202],
|
||||
[113.825642, 23.338205],
|
||||
[113.825641, 23.338207],
|
||||
[113.82564, 23.33821],
|
||||
[113.825639, 23.338212],
|
||||
[113.825639, 23.338214],
|
||||
[113.825638, 23.338217],
|
||||
[113.825637, 23.338219],
|
||||
[113.825636, 23.338221],
|
||||
[113.825635, 23.338224],
|
||||
[113.825635, 23.338226],
|
||||
[113.825634, 23.338229],
|
||||
[113.825633, 23.338231],
|
||||
[113.825632, 23.338233],
|
||||
[113.825632, 23.338236],
|
||||
[113.825631, 23.338238],
|
||||
[113.82563, 23.33824],
|
||||
[113.825629, 23.338243],
|
||||
[113.825628, 23.338245],
|
||||
[113.825629, 23.338248],
|
||||
[113.825628, 23.33825],
|
||||
[113.825627, 23.338252],
|
||||
[113.825626, 23.338255],
|
||||
[113.825626, 23.338257],
|
||||
[113.825625, 23.338259],
|
||||
[113.825624, 23.338262],
|
||||
[113.825623, 23.338265],
|
||||
[113.825623, 23.338268],
|
||||
[113.825622, 23.33827],
|
||||
[113.825621, 23.338272],
|
||||
[113.82562, 23.338275],
|
||||
[113.82562, 23.338277],
|
||||
[113.825619, 23.33828],
|
||||
[113.825618, 23.338282],
|
||||
[113.825618, 23.338284],
|
||||
[113.825617, 23.338287],
|
||||
[113.825616, 23.338289],
|
||||
[113.825615, 23.338292],
|
||||
[113.825532, 23.338833],
|
||||
[113.825532, 23.338835],
|
||||
[113.825532, 23.338838],
|
||||
[113.825532, 23.33884],
|
||||
[113.825532, 23.338843],
|
||||
[113.825532, 23.338845],
|
||||
[113.825531, 23.338848],
|
||||
[113.825532, 23.33885],
|
||||
[113.825532, 23.338853],
|
||||
[113.825532, 23.338855],
|
||||
[113.825532, 23.338858],
|
||||
[113.825532, 23.33886],
|
||||
[113.825532, 23.338863],
|
||||
[113.825531, 23.338865],
|
||||
[113.825531, 23.338868],
|
||||
[113.825531, 23.33887],
|
||||
[113.825531, 23.338873],
|
||||
[113.825531, 23.338875],
|
||||
[113.825531, 23.338878],
|
||||
[113.825532, 23.33888],
|
||||
[113.825532, 23.338883],
|
||||
[113.825531, 23.338885],
|
||||
[113.825531, 23.338888],
|
||||
[113.825531, 23.33889],
|
||||
[113.825531, 23.338893],
|
||||
[113.825531, 23.338895],
|
||||
[113.825531, 23.338898],
|
||||
[113.825531, 23.3389],
|
||||
[113.825531, 23.338903],
|
||||
[113.825531, 23.338905],
|
||||
[113.82553, 23.338908],
|
||||
[113.825531, 23.33891],
|
||||
[113.825531, 23.338913],
|
||||
[113.825531, 23.338915],
|
||||
[113.825531, 23.338918],
|
||||
[113.825531, 23.33892],
|
||||
[113.825531, 23.338923],
|
||||
[113.825531, 23.338925],
|
||||
[113.825531, 23.338928],
|
||||
[113.825531, 23.33893],
|
||||
[113.825531, 23.338933],
|
||||
[113.82553, 23.338935],
|
||||
[113.82553, 23.338938],
|
||||
[113.825531, 23.33894],
|
||||
[113.825531, 23.338942],
|
||||
[113.825531, 23.338945],
|
||||
[113.825531, 23.338947],
|
||||
[113.825531, 23.33895],
|
||||
[113.825531, 23.338952],
|
||||
[113.825531, 23.338956],
|
||||
[113.825531, 23.338958],
|
||||
[113.825531, 23.338961],
|
||||
[113.825531, 23.338963],
|
||||
[113.825531, 23.338966],
|
||||
[113.825532, 23.338968],
|
||||
[113.825532, 23.338971],
|
||||
[113.825532, 23.338973],
|
||||
[113.825531, 23.338976],
|
||||
[113.825531, 23.338978],
|
||||
[113.825531, 23.338981],
|
||||
[113.825531, 23.338983],
|
||||
[113.825531, 23.338986],
|
||||
[113.825531, 23.338988],
|
||||
[113.825531, 23.338991],
|
||||
[113.825531, 23.338993],
|
||||
[113.825531, 23.338996],
|
||||
[113.825532, 23.338998],
|
||||
[113.825532, 23.339001],
|
||||
[113.825532, 23.339003],
|
||||
[113.825532, 23.339006],
|
||||
[113.825532, 23.339008],
|
||||
[113.825532, 23.339011],
|
||||
[113.825532, 23.339013],
|
||||
[113.825532, 23.339016],
|
||||
[113.825532, 23.339018],
|
||||
[113.825532, 23.339021],
|
||||
[113.825532, 23.339023],
|
||||
[113.825533, 23.339026],
|
||||
[113.825533, 23.339028],
|
||||
[113.825533, 23.339031],
|
||||
[113.825533, 23.339033],
|
||||
[113.825533, 23.339036],
|
||||
[113.825533, 23.339038],
|
||||
[113.825533, 23.339041],
|
||||
[113.825533, 23.339043],
|
||||
[113.825533, 23.339046],
|
||||
[113.825533, 23.339048],
|
||||
[113.825533, 23.339051],
|
||||
[113.825533, 23.339053],
|
||||
[113.825534, 23.339056],
|
||||
[113.825534, 23.339058],
|
||||
[113.825534, 23.339061],
|
||||
[113.825534, 23.339063],
|
||||
[113.825534, 23.339066],
|
||||
[113.825534, 23.339068],
|
||||
[113.825534, 23.339071],
|
||||
[113.825534, 23.339073],
|
||||
[113.825534, 23.339076],
|
||||
[113.825534, 23.339078],
|
||||
[113.825534, 23.339081],
|
||||
[113.825535, 23.339083],
|
||||
[113.825535, 23.339086],
|
||||
[113.825535, 23.339088],
|
||||
[113.825535, 23.339091],
|
||||
[113.825535, 23.339093],
|
||||
[113.825535, 23.339096],
|
||||
[113.825535, 23.339098],
|
||||
[113.825535, 23.339101],
|
||||
[113.825535, 23.339103],
|
||||
[113.825535, 23.339106],
|
||||
[113.825535, 23.339108],
|
||||
[113.825536, 23.339111],
|
||||
[113.825536, 23.339113],
|
||||
[113.825536, 23.339116],
|
||||
[113.825536, 23.339118],
|
||||
[113.825536, 23.339121],
|
||||
[113.825536, 23.339123],
|
||||
[113.825536, 23.339125],
|
||||
[113.825536, 23.339128],
|
||||
[113.825536, 23.33913],
|
||||
[113.825536, 23.339133],
|
||||
[113.825536, 23.339135],
|
||||
[113.825537, 23.339138],
|
||||
[113.825537, 23.33914],
|
||||
[113.825538, 23.339143],
|
||||
[113.825538, 23.339145],
|
||||
[113.825538, 23.339148],
|
||||
[113.825538, 23.33915],
|
||||
[113.825538, 23.339153],
|
||||
[113.825538, 23.339156],
|
||||
[113.825538, 23.339159],
|
||||
[113.825538, 23.339161],
|
||||
[113.825538, 23.339164],
|
||||
[113.825539, 23.339166],
|
||||
[113.825539, 23.339169],
|
||||
[113.825539, 23.339171],
|
||||
[113.825539, 23.339174],
|
||||
[113.825539, 23.339176],
|
||||
[113.825539, 23.339179],
|
||||
[113.825539, 23.339181],
|
||||
[113.825539, 23.339184],
|
||||
[113.825539, 23.339186],
|
||||
[113.825539, 23.339189],
|
||||
[113.825539, 23.339191],
|
||||
[113.82554, 23.339194],
|
||||
[113.82554, 23.339196],
|
||||
[113.825541, 23.339199],
|
||||
[113.825541, 23.339201],
|
||||
[113.825541, 23.339204],
|
||||
[113.825541, 23.339206],
|
||||
[113.825541, 23.339209],
|
||||
[113.825541, 23.339211],
|
||||
[113.825541, 23.339214],
|
||||
[113.825541, 23.339216],
|
||||
[113.825542, 23.339219],
|
||||
[113.825542, 23.339221],
|
||||
[113.825542, 23.339224],
|
||||
[113.825542, 23.339226],
|
||||
[113.825542, 23.339229],
|
||||
[113.825542, 23.339231],
|
||||
[113.825542, 23.339234],
|
||||
[113.825542, 23.339236],
|
||||
[113.825542, 23.339239],
|
||||
[113.825542, 23.339241],
|
||||
[113.825543, 23.339244],
|
||||
[113.825544, 23.339246],
|
||||
[113.825544, 23.339249],
|
||||
[113.825544, 23.339251],
|
||||
[113.825544, 23.339254],
|
||||
[113.825544, 23.339256],
|
||||
[113.825544, 23.339259],
|
||||
[113.825544, 23.339261],
|
||||
[113.825544, 23.339264],
|
||||
[113.825544, 23.339266],
|
||||
[113.825544, 23.339269],
|
||||
[113.825544, 23.339271],
|
||||
[113.825545, 23.339274],
|
||||
[113.825545, 23.339276],
|
||||
[113.825545, 23.339279],
|
||||
[113.825545, 23.339281],
|
||||
[113.825545, 23.339284],
|
||||
[113.825675, 23.341405],
|
||||
[113.825676, 23.341408],
|
||||
[113.825676, 23.34141],
|
||||
[113.825676, 23.341413],
|
||||
[113.825676, 23.341415],
|
||||
[113.825676, 23.341418],
|
||||
[113.825676, 23.34142],
|
||||
[113.825676, 23.341423],
|
||||
[113.825676, 23.341425],
|
||||
[113.825676, 23.341428],
|
||||
[113.825676, 23.34143],
|
||||
[113.825676, 23.341433],
|
||||
[113.825676, 23.341435],
|
||||
[113.825677, 23.341438],
|
||||
[113.825677, 23.34144],
|
||||
[113.825677, 23.341443],
|
||||
[113.825678, 23.341445],
|
||||
[113.825678, 23.341448],
|
||||
[113.825678, 23.34145],
|
||||
[113.825678, 23.341453],
|
||||
[113.825678, 23.341455],
|
||||
[113.825678, 23.341458],
|
||||
[113.825678, 23.34146],
|
||||
[113.825679, 23.341463],
|
||||
[113.825679, 23.341465],
|
||||
[113.825679, 23.341468],
|
||||
[113.825679, 23.34147],
|
||||
[113.825679, 23.341473],
|
||||
[113.825679, 23.341475],
|
||||
[113.825679, 23.341478],
|
||||
[113.825679, 23.34148],
|
||||
[113.825679, 23.341483],
|
||||
[113.825679, 23.341485],
|
||||
[113.82568, 23.341488],
|
||||
[113.825681, 23.34149],
|
||||
[113.825681, 23.341493],
|
||||
[113.825681, 23.341495],
|
||||
[113.825681, 23.341498],
|
||||
[113.825681, 23.3415],
|
||||
[113.825681, 23.341503],
|
||||
[113.825681, 23.341505],
|
||||
[113.825681, 23.341508],
|
||||
[113.825681, 23.34151],
|
||||
[113.825681, 23.341513],
|
||||
[113.825681, 23.341515],
|
||||
[113.825682, 23.341518],
|
||||
[113.825682, 23.34152],
|
||||
[113.825682, 23.341524],
|
||||
[113.825682, 23.341526],
|
||||
[113.825682, 23.341529],
|
||||
[113.825682, 23.341531],
|
||||
[113.825682, 23.341534],
|
||||
[113.825683, 23.341536],
|
||||
[113.825683, 23.341539],
|
||||
[113.825683, 23.341541],
|
||||
[113.825684, 23.341544],
|
||||
[113.825684, 23.341546],
|
||||
[113.825684, 23.341549],
|
||||
[113.825684, 23.341551],
|
||||
[113.825684, 23.341554],
|
||||
[113.825684, 23.341556],
|
||||
[113.825684, 23.341558],
|
||||
[113.825684, 23.341561],
|
||||
[113.825684, 23.341563],
|
||||
[113.825684, 23.341566],
|
||||
[113.825684, 23.341568],
|
||||
[113.825684, 23.341571],
|
||||
[113.825685, 23.341573],
|
||||
[113.825685, 23.341576],
|
||||
[113.825685, 23.341578],
|
||||
[113.825685, 23.341581],
|
||||
[113.825685, 23.341583],
|
||||
[113.825686, 23.341586],
|
||||
[113.825686, 23.341588],
|
||||
[113.825686, 23.341591],
|
||||
[113.825686, 23.341593],
|
||||
[113.825686, 23.341596],
|
||||
[113.825687, 23.341598],
|
||||
[113.825687, 23.341601],
|
||||
[113.825687, 23.341603],
|
||||
[113.825687, 23.341606],
|
||||
[113.825687, 23.341608],
|
||||
[113.825687, 23.341611],
|
||||
[113.825687, 23.341613],
|
||||
[113.825687, 23.341616],
|
||||
[113.825687, 23.341618],
|
||||
[113.825687, 23.341621],
|
||||
[113.825687, 23.341623],
|
||||
[113.825688, 23.341626],
|
||||
[113.825688, 23.341628],
|
||||
[113.825688, 23.341631],
|
||||
[113.825688, 23.341633],
|
||||
[113.825688, 23.341636],
|
||||
[113.825688, 23.341638],
|
||||
[113.825688, 23.341641],
|
||||
[113.825688, 23.341643],
|
||||
[113.825688, 23.341646],
|
||||
[113.825688, 23.341648],
|
||||
[113.825689, 23.341651],
|
||||
[113.82569, 23.341653],
|
||||
[113.82569, 23.341656],
|
||||
[113.82569, 23.341658],
|
||||
[113.82569, 23.341661],
|
||||
[113.82569, 23.341663],
|
||||
[113.82569, 23.341666],
|
||||
[113.82569, 23.341668],
|
||||
[113.82569, 23.341671],
|
||||
[113.82569, 23.341673],
|
||||
[113.82569, 23.341676],
|
||||
[113.82569, 23.341678],
|
||||
[113.825691, 23.341681],
|
||||
[113.825691, 23.341683],
|
||||
[113.825691, 23.341686],
|
||||
[113.825691, 23.341688],
|
||||
[113.825691, 23.341691],
|
||||
[113.825691, 23.341693],
|
||||
[113.825691, 23.341696],
|
||||
[113.825691, 23.341698],
|
||||
[113.825691, 23.341701],
|
||||
[113.825691, 23.341703],
|
||||
[113.825691, 23.341706],
|
||||
[113.825691, 23.341708],
|
||||
[113.825692, 23.341711],
|
||||
[113.825692, 23.341713],
|
||||
[113.825692, 23.341716],
|
||||
[113.825692, 23.341718],
|
||||
[113.825692, 23.341721],
|
||||
[113.825692, 23.341723],
|
||||
[113.825692, 23.341726],
|
||||
[113.825692, 23.341728],
|
||||
[113.825692, 23.341731],
|
||||
[113.825692, 23.341733],
|
||||
[113.825693, 23.341736],
|
||||
[113.825693, 23.341738],
|
||||
[113.825693, 23.34174],
|
||||
[113.825693, 23.341744],
|
||||
[113.825693, 23.341746],
|
||||
[113.825693, 23.341749],
|
||||
[113.825693, 23.341751],
|
||||
[113.825693, 23.341754],
|
||||
[113.825693, 23.341756],
|
||||
[113.825693, 23.341759],
|
||||
[113.825693, 23.341761],
|
||||
[113.825693, 23.341764],
|
||||
[113.825694, 23.341766],
|
||||
[113.825694, 23.341769],
|
||||
[113.825694, 23.341771],
|
||||
[113.825694, 23.341774],
|
||||
[113.825694, 23.341776],
|
||||
[113.825694, 23.341779],
|
||||
[113.825694, 23.341781],
|
||||
[113.825694, 23.341784],
|
||||
[113.825694, 23.341786],
|
||||
[113.825694, 23.341789],
|
||||
[113.825694, 23.341791],
|
||||
[113.825695, 23.341794],
|
||||
[113.825695, 23.341796],
|
||||
[113.825695, 23.341799],
|
||||
[113.825695, 23.341801],
|
||||
[113.825695, 23.341804],
|
||||
[113.825695, 23.341806],
|
||||
[113.825695, 23.341809],
|
||||
[113.825695, 23.341811],
|
||||
[113.825695, 23.341814],
|
||||
[113.825695, 23.341816],
|
||||
[113.825695, 23.341819],
|
||||
[113.825696, 23.341821],
|
||||
[113.825696, 23.341824],
|
||||
[113.825696, 23.341826],
|
||||
[113.825696, 23.341829],
|
||||
[113.825696, 23.341831],
|
||||
[113.825696, 23.341834],
|
||||
[113.825696, 23.341836],
|
||||
[113.825696, 23.341839],
|
||||
[113.825696, 23.341841],
|
||||
[113.825696, 23.341844],
|
||||
[113.825696, 23.341846],
|
||||
[113.825697, 23.341849],
|
||||
[113.825697, 23.341851],
|
||||
[113.825697, 23.341854],
|
||||
[113.825696, 23.341856],
|
||||
[113.825639, 23.342752],
|
||||
[113.825909, 23.343038],
|
||||
[113.826124, 23.34317],
|
||||
[113.826246, 23.343207],
|
||||
[113.826544, 23.343462],
|
||||
[113.826663, 23.343725],
|
||||
[113.826921, 23.344028],
|
||||
[113.826946, 23.344045],
|
||||
[113.827411, 23.344775],
|
||||
[113.827452, 23.345071],
|
||||
[113.827433, 23.345444],
|
||||
[113.827373, 23.345688],
|
||||
[113.827847, 23.346486],
|
||||
[113.827867, 23.346492],
|
||||
[113.828217, 23.346555],
|
||||
[113.828812, 23.346578],
|
||||
[113.829478, 23.346804],
|
||||
[113.829926, 23.347127],
|
||||
[113.830393, 23.34749],
|
||||
[113.830756, 23.347827],
|
||||
[113.83112, 23.348231],
|
||||
[113.832415, 23.349689],
|
||||
[113.833006, 23.350048],
|
||||
[113.834533, 23.350448],
|
||||
[113.835235, 23.350919],
|
||||
[113.835526, 23.35132],
|
||||
[113.836805, 23.353126],
|
||||
[113.836931, 23.353474],
|
||||
[113.837019, 23.35471],
|
||||
[113.837041, 23.354934],
|
||||
[113.837129, 23.355633],
|
||||
[113.837135, 23.355731],
|
||||
[113.837148, 23.35729],
|
||||
[113.837124, 23.357391],
|
||||
[113.837093, 23.357543],
|
||||
[113.837116, 23.357842],
|
||||
[113.837231, 23.358547],
|
||||
[113.837488, 23.359569],
|
||||
[113.837398, 23.359954],
|
||||
[113.838019, 23.360873],
|
||||
[113.838056, 23.360928],
|
||||
[113.840391, 23.36073]
|
||||
]
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"center": {
|
||||
"lat": 0,
|
||||
"lon": 0
|
||||
},
|
||||
"smuserid": 960,
|
||||
"area": 2807456.367185,
|
||||
"refname": "10301",
|
||||
"name": "",
|
||||
"refid": 293,
|
||||
"districtid": 440183,
|
||||
"ver": "v1.20170705",
|
||||
"class": "–°«¯",
|
||||
"id": 960,
|
||||
"verdate": "20170705",
|
||||
"oriid": 1724
|
||||
}
|
||||
}
|
||||
`
|
||||
poly := testJSON(t, json).(Feature)
|
||||
g, _ := ObjectJSON(`{"type":"Point","coordinates":[113.837019, 23.35471]}`)
|
||||
if !g.Within(poly) {
|
||||
t.Fatal("!")
|
||||
}
|
||||
g, _ = ObjectJSON(`{"type":"Point","coordinates":[114.837019, 23.35471]}`)
|
||||
if g.Within(poly) {
|
||||
t.Fatal("!")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
package geojson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/tile38/geojson/geohash"
|
||||
)
|
||||
|
||||
// FeatureCollection is a geojson object with the type "FeatureCollection"
|
||||
type FeatureCollection struct {
|
||||
Features []Object
|
||||
BBox *BBox
|
||||
Features []Object
|
||||
BBox *BBox
|
||||
bboxDefined bool
|
||||
}
|
||||
|
||||
func fillFeatureCollectionMap(json string) (FeatureCollection, error) {
|
||||
|
@ -40,6 +39,14 @@ func fillFeatureCollectionMap(json string) (FeatureCollection, error) {
|
|||
}
|
||||
var err error
|
||||
g.BBox, err = fillBBox(json)
|
||||
if err != nil {
|
||||
return g, err
|
||||
}
|
||||
g.bboxDefined = g.BBox != nil
|
||||
if !g.bboxDefined {
|
||||
cbbox := g.CalculatedBBox()
|
||||
g.BBox = &cbbox
|
||||
}
|
||||
return g, err
|
||||
}
|
||||
|
||||
|
@ -93,23 +100,27 @@ func (g FeatureCollection) Weight() int {
|
|||
|
||||
// MarshalJSON allows the object to be encoded in json.Marshal calls.
|
||||
func (g FeatureCollection) MarshalJSON() ([]byte, error) {
|
||||
return []byte(g.JSON()), nil
|
||||
return g.appendJSON(nil), nil
|
||||
}
|
||||
|
||||
func (g FeatureCollection) appendJSON(json []byte) []byte {
|
||||
json = append(json, `{"type":"FeatureCollection","features":[`...)
|
||||
for i, g := range g.Features {
|
||||
if i != 0 {
|
||||
json = append(json, ',')
|
||||
}
|
||||
json = append(json, g.JSON()...)
|
||||
}
|
||||
json = append(json, ']')
|
||||
if g.bboxDefined {
|
||||
json = appendBBoxJSON(json, g.BBox)
|
||||
}
|
||||
return append(json, '}')
|
||||
}
|
||||
|
||||
// JSON is the json representation of the object. This might not be exactly the same as the original.
|
||||
func (g FeatureCollection) JSON() string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(`{"type":"FeatureCollection","features":[`)
|
||||
for i, g := range g.Features {
|
||||
if i != 0 {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
buf.WriteString(g.JSON())
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
g.BBox.write(&buf)
|
||||
buf.WriteByte('}')
|
||||
return buf.String()
|
||||
return string(g.appendJSON(nil))
|
||||
}
|
||||
|
||||
// String returns a string representation of the object. This might be JSON or something else.
|
||||
|
@ -138,7 +149,7 @@ func (g FeatureCollection) hasPositions() bool {
|
|||
|
||||
// WithinBBox detects if the object is fully contained inside a bbox.
|
||||
func (g FeatureCollection) WithinBBox(bbox BBox) bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox))
|
||||
}
|
||||
if len(g.Features) == 0 {
|
||||
|
@ -154,7 +165,7 @@ func (g FeatureCollection) WithinBBox(bbox BBox) bool {
|
|||
|
||||
// IntersectsBBox detects if the object intersects a bbox.
|
||||
func (g FeatureCollection) IntersectsBBox(bbox BBox) bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox))
|
||||
}
|
||||
for _, g := range g.Features {
|
||||
|
@ -228,7 +239,7 @@ func (g FeatureCollection) Nearby(center Position, meters float64) bool {
|
|||
|
||||
// IsBBoxDefined returns true if the object has a defined bbox.
|
||||
func (g FeatureCollection) IsBBoxDefined() bool {
|
||||
return g.BBox != nil
|
||||
return g.bboxDefined
|
||||
}
|
||||
|
||||
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
|
||||
|
|
|
@ -40,6 +40,7 @@ func doesJSONMatch(js1, js2 string) bool {
|
|||
}
|
||||
|
||||
func testJSON(t *testing.T, jstr string) Object {
|
||||
t.Helper()
|
||||
o, err := ObjectJSON(jstr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -47,11 +48,11 @@ func testJSON(t *testing.T, jstr string) Object {
|
|||
if !doesJSONMatch(o.JSON(), jstr) {
|
||||
t.Fatalf("expected '%v', got '%v'", o.JSON(), jstr)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
func testInvalidJSON(t *testing.T, js string, expecting error) {
|
||||
t.Helper()
|
||||
_, err := ObjectJSON(js)
|
||||
if err == nil {
|
||||
t.Fatalf("expecting an error for json '%s'", js)
|
||||
|
@ -86,33 +87,3 @@ func TestInvalidJSON(t *testing.T) {
|
|||
testInvalidJSON(t, `{"type":"Point","coordinates":[1,2,3],"bbox":[1,2,3]}`, errBBoxInvalidNumberOfValues)
|
||||
testInvalidJSON(t, `{"type":"Point","coordinates":[1,2,3],"bbox":[1,2,3,"a"]}`, errBBoxInvalidValue)
|
||||
}
|
||||
|
||||
// func TestJunk(t *testing.T) {
|
||||
// type ThreePoint struct{ X, Y, Z float64 }
|
||||
|
||||
// var s1 = ThreePoint{50, 50, 50}
|
||||
// var s2 = &s1
|
||||
// var o1 interface{} = s1
|
||||
// var o2 interface{} = s2
|
||||
|
||||
// t1 := reflect.TypeOf(s1)
|
||||
// t2 := reflect.TypeOf(s2)
|
||||
// t3 := reflect.TypeOf(o1)
|
||||
// t4 := reflect.TypeOf(o2)
|
||||
|
||||
// fmt.Printf("typeof: %s\n", t1)
|
||||
// fmt.Printf("typeof: %s\n", t2)
|
||||
// fmt.Printf("typeof: %s\n", t3)
|
||||
// fmt.Printf("typeof: %s\n", t4)
|
||||
|
||||
// z1 := unsafe.Sizeof(s1)
|
||||
// z2 := unsafe.Sizeof(s2)
|
||||
// z3 := unsafe.Sizeof(o1)
|
||||
// z4 := unsafe.Sizeof(o2.(*ThreePoint))
|
||||
|
||||
// fmt.Printf("sizeof: %d\n", z1)
|
||||
// fmt.Printf("sizeof: %d\n", z2)
|
||||
// fmt.Printf("sizeof: %d\n", z3)
|
||||
// fmt.Printf("sizeof: %d\n", z4)
|
||||
|
||||
// }
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
package geojson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/tile38/geojson/geohash"
|
||||
)
|
||||
|
||||
// GeometryCollection is a geojson object with the type "GeometryCollection"
|
||||
type GeometryCollection struct {
|
||||
Geometries []Object
|
||||
BBox *BBox
|
||||
Geometries []Object
|
||||
BBox *BBox
|
||||
bboxDefined bool
|
||||
}
|
||||
|
||||
func fillGeometryCollectionMap(json string) (GeometryCollection, error) {
|
||||
|
@ -40,6 +39,14 @@ func fillGeometryCollectionMap(json string) (GeometryCollection, error) {
|
|||
}
|
||||
var err error
|
||||
g.BBox, err = fillBBox(json)
|
||||
if err != nil {
|
||||
return g, err
|
||||
}
|
||||
g.bboxDefined = g.BBox != nil
|
||||
if !g.bboxDefined {
|
||||
cbbox := g.CalculatedBBox()
|
||||
g.BBox = &cbbox
|
||||
}
|
||||
return g, err
|
||||
}
|
||||
|
||||
|
@ -93,23 +100,26 @@ func (g GeometryCollection) Weight() int {
|
|||
|
||||
// MarshalJSON allows the object to be encoded in json.Marshal calls.
|
||||
func (g GeometryCollection) MarshalJSON() ([]byte, error) {
|
||||
return []byte(g.JSON()), nil
|
||||
return g.appendJSON(nil), nil
|
||||
}
|
||||
func (g GeometryCollection) appendJSON(json []byte) []byte {
|
||||
json = append(json, `{"type":"GeometryCollection","geometries":[`...)
|
||||
for i, g := range g.Geometries {
|
||||
if i != 0 {
|
||||
json = append(json, ',')
|
||||
}
|
||||
json = append(json, g.JSON()...)
|
||||
}
|
||||
json = append(json, ']')
|
||||
if g.bboxDefined {
|
||||
json = appendBBoxJSON(json, g.BBox)
|
||||
}
|
||||
return append(json, '}')
|
||||
}
|
||||
|
||||
// JSON is the json representation of the object. This might not be exactly the same as the original.
|
||||
func (g GeometryCollection) JSON() string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(`{"type":"GeometryCollection","geometries":[`)
|
||||
for i, g := range g.Geometries {
|
||||
if i != 0 {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
buf.WriteString(g.JSON())
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
g.BBox.write(&buf)
|
||||
buf.WriteByte('}')
|
||||
return buf.String()
|
||||
return string(g.appendJSON(nil))
|
||||
}
|
||||
|
||||
// String returns a string representation of the object. This might be JSON or something else.
|
||||
|
@ -138,7 +148,7 @@ func (g GeometryCollection) hasPositions() bool {
|
|||
|
||||
// WithinBBox detects if the object is fully contained inside a bbox.
|
||||
func (g GeometryCollection) WithinBBox(bbox BBox) bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox))
|
||||
}
|
||||
if len(g.Geometries) == 0 {
|
||||
|
@ -154,7 +164,7 @@ func (g GeometryCollection) WithinBBox(bbox BBox) bool {
|
|||
|
||||
// IntersectsBBox detects if the object intersects a bbox.
|
||||
func (g GeometryCollection) IntersectsBBox(bbox BBox) bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox))
|
||||
}
|
||||
for _, g := range g.Geometries {
|
||||
|
@ -228,7 +238,7 @@ func (g GeometryCollection) Nearby(center Position, meters float64) bool {
|
|||
|
||||
// IsBBoxDefined returns true if the object has a defined bbox.
|
||||
func (g GeometryCollection) IsBBoxDefined() bool {
|
||||
return g.BBox != nil
|
||||
return g.bboxDefined
|
||||
}
|
||||
|
||||
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
package geojson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
import "github.com/tidwall/gjson"
|
||||
|
||||
func resIsArray(res gjson.Result) bool {
|
||||
if res.Type == gjson.JSON {
|
||||
|
@ -71,17 +67,20 @@ func level1Weight(coordinates Position, bbox *BBox) int {
|
|||
return level1PositionCount(coordinates, bbox) * sizeofPosition
|
||||
}
|
||||
|
||||
func level1JSON(name string, coordinates Position, bbox *BBox) string {
|
||||
func appendLevel1JSON(json []byte, name string, coordinates Position, bbox *BBox, bboxDefined bool) []byte {
|
||||
if bbox != nil && !bboxDefined {
|
||||
bbox = nil
|
||||
}
|
||||
isCordZ := level1IsCoordZDefined(coordinates, bbox)
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(`{"type":"`)
|
||||
buf.WriteString(name)
|
||||
buf.WriteString(`","coordinates":[`)
|
||||
coordinates.writeJSON(&buf, isCordZ)
|
||||
buf.WriteByte(']')
|
||||
bbox.write(&buf)
|
||||
buf.WriteByte('}')
|
||||
return buf.String()
|
||||
json = append(json, `{"type":"`...)
|
||||
json = append(json, name...)
|
||||
json = append(json, `","coordinates":[`...)
|
||||
json = appendPositionJSON(json, coordinates, isCordZ)
|
||||
json = append(json, ']')
|
||||
if bboxDefined {
|
||||
json = appendBBoxJSON(json, bbox)
|
||||
}
|
||||
return append(json, '}')
|
||||
}
|
||||
|
||||
func level1IsCoordZDefined(coordinates Position, bbox *BBox) bool {
|
||||
|
@ -149,24 +148,28 @@ func level2Weight(coordinates []Position, bbox *BBox) int {
|
|||
return level2PositionCount(coordinates, bbox) * sizeofPosition
|
||||
}
|
||||
|
||||
func level2JSON(name string, coordinates []Position, bbox *BBox) string {
|
||||
func appendLevel2JSON(json []byte, name string, coordinates []Position, bbox *BBox, bboxDefined bool) []byte {
|
||||
if bbox != nil && !bboxDefined {
|
||||
bbox = nil
|
||||
}
|
||||
isCordZ := level2IsCoordZDefined(coordinates, bbox)
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(`{"type":"`)
|
||||
buf.WriteString(name)
|
||||
buf.WriteString(`","coordinates":[`)
|
||||
json = append(json, `{"type":"`...)
|
||||
json = append(json, name...)
|
||||
json = append(json, `","coordinates":[`...)
|
||||
for i, p := range coordinates {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
json = append(json, ',')
|
||||
}
|
||||
buf.WriteByte('[')
|
||||
p.writeJSON(&buf, isCordZ)
|
||||
buf.WriteByte(']')
|
||||
json = append(json, '[')
|
||||
json = appendPositionJSON(json, p, isCordZ)
|
||||
json = append(json, ']')
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
bbox.write(&buf)
|
||||
buf.WriteByte('}')
|
||||
return buf.String()
|
||||
json = append(json, ']')
|
||||
if bboxDefined {
|
||||
json = appendBBoxJSON(json, bbox)
|
||||
}
|
||||
json = append(json, '}')
|
||||
return json
|
||||
}
|
||||
|
||||
func level2IsCoordZDefined(coordinates []Position, bbox *BBox) bool {
|
||||
|
@ -259,31 +262,34 @@ func level3PositionCount(coordinates [][]Position, bbox *BBox) int {
|
|||
return res
|
||||
}
|
||||
|
||||
func level3JSON(name string, coordinates [][]Position, bbox *BBox) string {
|
||||
func appendLevel3JSON(json []byte, name string, coordinates [][]Position, bbox *BBox, bboxDefined bool) []byte {
|
||||
if bbox != nil && !bboxDefined {
|
||||
bbox = nil
|
||||
}
|
||||
isCordZ := level3IsCoordZDefined(coordinates, bbox)
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(`{"type":"`)
|
||||
buf.WriteString(name)
|
||||
buf.WriteString(`","coordinates":[`)
|
||||
json = append(json, `{"type":"`...)
|
||||
json = append(json, name...)
|
||||
json = append(json, `","coordinates":[`...)
|
||||
for i, p := range coordinates {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
json = append(json, ',')
|
||||
}
|
||||
buf.WriteByte('[')
|
||||
json = append(json, '[')
|
||||
for i, p := range p {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
json = append(json, ',')
|
||||
}
|
||||
buf.WriteByte('[')
|
||||
p.writeJSON(&buf, isCordZ)
|
||||
buf.WriteByte(']')
|
||||
json = append(json, '[')
|
||||
json = appendPositionJSON(json, p, isCordZ)
|
||||
json = append(json, ']')
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
json = append(json, ']')
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
bbox.write(&buf)
|
||||
buf.WriteByte('}')
|
||||
return buf.String()
|
||||
json = append(json, ']')
|
||||
if bboxDefined {
|
||||
json = appendBBoxJSON(json, bbox)
|
||||
}
|
||||
return append(json, '}')
|
||||
}
|
||||
|
||||
func level3IsCoordZDefined(coordinates [][]Position, bbox *BBox) bool {
|
||||
|
@ -388,38 +394,41 @@ func level4PositionCount(coordinates [][][]Position, bbox *BBox) int {
|
|||
return res
|
||||
}
|
||||
|
||||
func level4JSON(name string, coordinates [][][]Position, bbox *BBox) string {
|
||||
func appendLevel4JSON(json []byte, name string, coordinates [][][]Position, bbox *BBox, bboxDefined bool) []byte {
|
||||
if bbox != nil && !bboxDefined {
|
||||
bbox = nil
|
||||
}
|
||||
isCordZ := level4IsCoordZDefined(coordinates, bbox)
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(`{"type":"`)
|
||||
buf.WriteString(name)
|
||||
buf.WriteString(`","coordinates":[`)
|
||||
json = append(json, `{"type":"`...)
|
||||
json = append(json, name...)
|
||||
json = append(json, `","coordinates":[`...)
|
||||
for i, p := range coordinates {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
json = append(json, ',')
|
||||
}
|
||||
buf.WriteByte('[')
|
||||
json = append(json, '[')
|
||||
for i, p := range p {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
json = append(json, ',')
|
||||
}
|
||||
buf.WriteByte('[')
|
||||
json = append(json, '[')
|
||||
for i, p := range p {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
json = append(json, ',')
|
||||
}
|
||||
buf.WriteByte('[')
|
||||
p.writeJSON(&buf, isCordZ)
|
||||
buf.WriteByte(']')
|
||||
json = append(json, '[')
|
||||
json = appendPositionJSON(json, p, isCordZ)
|
||||
json = append(json, ']')
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
json = append(json, ']')
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
json = append(json, ']')
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
bbox.write(&buf)
|
||||
buf.WriteByte('}')
|
||||
return buf.String()
|
||||
json = append(json, ']')
|
||||
if bboxDefined {
|
||||
json = appendBBoxJSON(json, bbox)
|
||||
}
|
||||
return append(json, '}')
|
||||
}
|
||||
|
||||
func level4IsCoordZDefined(coordinates [][][]Position, bbox *BBox) bool {
|
||||
|
|
|
@ -6,6 +6,7 @@ import "github.com/tidwall/tile38/geojson/geohash"
|
|||
type LineString struct {
|
||||
Coordinates []Position
|
||||
BBox *BBox
|
||||
bboxDefined bool
|
||||
}
|
||||
|
||||
func fillLineString(coordinates []Position, bbox *BBox, err error) (LineString, error) {
|
||||
|
@ -14,9 +15,15 @@ func fillLineString(coordinates []Position, bbox *BBox, err error) (LineString,
|
|||
err = errLineStringInvalidCoordinates
|
||||
}
|
||||
}
|
||||
bboxDefined := bbox != nil
|
||||
if !bboxDefined {
|
||||
cbbox := level2CalculatedBBox(coordinates, nil)
|
||||
bbox = &cbbox
|
||||
}
|
||||
return LineString{
|
||||
Coordinates: coordinates,
|
||||
BBox: bbox,
|
||||
bboxDefined: bboxDefined,
|
||||
}, err
|
||||
}
|
||||
|
||||
|
@ -46,14 +53,18 @@ func (g LineString) Weight() int {
|
|||
return level2Weight(g.Coordinates, g.BBox)
|
||||
}
|
||||
|
||||
func (g LineString) appendJSON(json []byte) []byte {
|
||||
return appendLevel2JSON(json, "LineString", g.Coordinates, g.BBox, g.bboxDefined)
|
||||
}
|
||||
|
||||
// MarshalJSON allows the object to be encoded in json.Marshal calls.
|
||||
func (g LineString) MarshalJSON() ([]byte, error) {
|
||||
return []byte(g.JSON()), nil
|
||||
return g.appendJSON(nil), nil
|
||||
}
|
||||
|
||||
// JSON is the json representation of the object. This might not be exactly the same as the original.
|
||||
func (g LineString) JSON() string {
|
||||
return level2JSON("LineString", g.Coordinates, g.BBox)
|
||||
return string(g.appendJSON(nil))
|
||||
}
|
||||
|
||||
// String returns a string representation of the object. This might be JSON or something else.
|
||||
|
@ -64,13 +75,14 @@ func (g LineString) String() string {
|
|||
func (g LineString) bboxPtr() *BBox {
|
||||
return g.BBox
|
||||
}
|
||||
|
||||
func (g LineString) hasPositions() bool {
|
||||
return g.BBox != nil || len(g.Coordinates) > 0
|
||||
return g.bboxDefined || len(g.Coordinates) > 0
|
||||
}
|
||||
|
||||
// WithinBBox detects if the object is fully contained inside a bbox.
|
||||
func (g LineString) WithinBBox(bbox BBox) bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox))
|
||||
}
|
||||
return polyPositions(g.Coordinates).InsideRect(rectBBox(bbox))
|
||||
|
@ -78,7 +90,7 @@ func (g LineString) WithinBBox(bbox BBox) bool {
|
|||
|
||||
// IntersectsBBox detects if the object intersects a bbox.
|
||||
func (g LineString) IntersectsBBox(bbox BBox) bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox))
|
||||
}
|
||||
return polyPositions(g.Coordinates).IntersectsRect(rectBBox(bbox))
|
||||
|
@ -125,7 +137,7 @@ func (g LineString) Nearby(center Position, meters float64) bool {
|
|||
|
||||
// IsBBoxDefined returns true if the object has a defined bbox.
|
||||
func (g LineString) IsBBoxDefined() bool {
|
||||
return g.BBox != nil
|
||||
return g.bboxDefined
|
||||
}
|
||||
|
||||
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
type MultiLineString struct {
|
||||
Coordinates [][]Position
|
||||
BBox *BBox
|
||||
bboxDefined bool
|
||||
}
|
||||
|
||||
func fillMultiLineString(coordinates [][]Position, bbox *BBox, err error) (MultiLineString, error) {
|
||||
|
@ -20,9 +21,15 @@ func fillMultiLineString(coordinates [][]Position, bbox *BBox, err error) (Multi
|
|||
}
|
||||
}
|
||||
}
|
||||
bboxDefined := bbox != nil
|
||||
if !bboxDefined {
|
||||
cbbox := level3CalculatedBBox(coordinates, nil, false)
|
||||
bbox = &cbbox
|
||||
}
|
||||
return MultiLineString{
|
||||
Coordinates: coordinates,
|
||||
BBox: bbox,
|
||||
bboxDefined: bboxDefined,
|
||||
}, err
|
||||
}
|
||||
|
||||
|
@ -54,12 +61,16 @@ func (g MultiLineString) Weight() int {
|
|||
|
||||
// MarshalJSON allows the object to be encoded in json.Marshal calls.
|
||||
func (g MultiLineString) MarshalJSON() ([]byte, error) {
|
||||
return []byte(g.JSON()), nil
|
||||
return g.appendJSON(nil), nil
|
||||
}
|
||||
|
||||
func (g MultiLineString) appendJSON(json []byte) []byte {
|
||||
return appendLevel3JSON(json, "MultiLineString", g.Coordinates, g.BBox, g.bboxDefined)
|
||||
}
|
||||
|
||||
// JSON is the json representation of the object. This might not be exactly the same as the original.
|
||||
func (g MultiLineString) JSON() string {
|
||||
return level3JSON("MultiLineString", g.Coordinates, g.BBox)
|
||||
return string(g.appendJSON(nil))
|
||||
}
|
||||
|
||||
// String returns a string representation of the object. This might be JSON or something else.
|
||||
|
@ -71,7 +82,7 @@ func (g MultiLineString) bboxPtr() *BBox {
|
|||
return g.BBox
|
||||
}
|
||||
func (g MultiLineString) hasPositions() bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return true
|
||||
}
|
||||
for _, c := range g.Coordinates {
|
||||
|
@ -84,7 +95,7 @@ func (g MultiLineString) hasPositions() bool {
|
|||
|
||||
// WithinBBox detects if the object is fully contained inside a bbox.
|
||||
func (g MultiLineString) WithinBBox(bbox BBox) bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox))
|
||||
}
|
||||
if len(g.Coordinates) == 0 {
|
||||
|
@ -105,7 +116,7 @@ func (g MultiLineString) WithinBBox(bbox BBox) bool {
|
|||
|
||||
// IntersectsBBox detects if the object intersects a bbox.
|
||||
func (g MultiLineString) IntersectsBBox(bbox BBox) bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox))
|
||||
}
|
||||
for _, ls := range g.Coordinates {
|
||||
|
@ -183,7 +194,7 @@ func (g MultiLineString) Nearby(center Position, meters float64) bool {
|
|||
|
||||
// IsBBoxDefined returns true if the object has a defined bbox.
|
||||
func (g MultiLineString) IsBBoxDefined() bool {
|
||||
return g.BBox != nil
|
||||
return g.bboxDefined
|
||||
}
|
||||
|
||||
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package geojson
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMultiLineString(t *testing.T) {
|
||||
testJSON(t, `{"type":"MultiLineString","coordinates":[[[100.1,5.1],[101.1,6.1]],[[102.1,7.1],[103.1,8.1]]]}`)
|
||||
|
|
|
@ -9,12 +9,19 @@ import (
|
|||
type MultiPoint struct {
|
||||
Coordinates []Position
|
||||
BBox *BBox
|
||||
bboxDefined bool
|
||||
}
|
||||
|
||||
func fillMultiPoint(coordinates []Position, bbox *BBox, err error) (MultiPoint, error) {
|
||||
bboxDefined := bbox != nil
|
||||
if !bboxDefined {
|
||||
cbbox := level2CalculatedBBox(coordinates, nil)
|
||||
bbox = &cbbox
|
||||
}
|
||||
return MultiPoint{
|
||||
Coordinates: coordinates,
|
||||
BBox: bbox,
|
||||
bboxDefined: bboxDefined,
|
||||
}, err
|
||||
}
|
||||
|
||||
|
@ -46,12 +53,16 @@ func (g MultiPoint) Weight() int {
|
|||
|
||||
// MarshalJSON allows the object to be encoded in json.Marshal calls.
|
||||
func (g MultiPoint) MarshalJSON() ([]byte, error) {
|
||||
return []byte(g.JSON()), nil
|
||||
return g.appendJSON(nil), nil
|
||||
}
|
||||
|
||||
func (g MultiPoint) appendJSON(json []byte) []byte {
|
||||
return appendLevel2JSON(json, "MultiPoint", g.Coordinates, g.BBox, g.bboxDefined)
|
||||
}
|
||||
|
||||
// JSON is the json representation of the object. This might not be exactly the same as the original.
|
||||
func (g MultiPoint) JSON() string {
|
||||
return level2JSON("MultiPoint", g.Coordinates, g.BBox)
|
||||
return string(g.appendJSON(nil))
|
||||
}
|
||||
|
||||
// String returns a string representation of the object. This might be JSON or something else.
|
||||
|
@ -63,12 +74,12 @@ func (g MultiPoint) bboxPtr() *BBox {
|
|||
return g.BBox
|
||||
}
|
||||
func (g MultiPoint) hasPositions() bool {
|
||||
return g.BBox != nil || len(g.Coordinates) > 0
|
||||
return g.bboxDefined || len(g.Coordinates) > 0
|
||||
}
|
||||
|
||||
// WithinBBox detects if the object is fully contained inside a bbox.
|
||||
func (g MultiPoint) WithinBBox(bbox BBox) bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox))
|
||||
}
|
||||
if len(g.Coordinates) == 0 {
|
||||
|
@ -162,7 +173,7 @@ func (g MultiPoint) Nearby(center Position, meters float64) bool {
|
|||
|
||||
// IsBBoxDefined returns true if the object has a defined bbox.
|
||||
func (g MultiPoint) IsBBoxDefined() bool {
|
||||
return g.BBox != nil
|
||||
return g.bboxDefined
|
||||
}
|
||||
|
||||
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
|
||||
|
|
|
@ -6,6 +6,7 @@ import "github.com/tidwall/tile38/geojson/geohash"
|
|||
type MultiPolygon struct {
|
||||
Coordinates [][][]Position
|
||||
BBox *BBox
|
||||
bboxDefined bool
|
||||
}
|
||||
|
||||
func fillMultiPolygon(coordinates [][][]Position, bbox *BBox, err error) (MultiPolygon, error) {
|
||||
|
@ -24,9 +25,15 @@ func fillMultiPolygon(coordinates [][][]Position, bbox *BBox, err error) (MultiP
|
|||
}
|
||||
}
|
||||
}
|
||||
bboxDefined := bbox != nil
|
||||
if !bboxDefined {
|
||||
cbbox := level4CalculatedBBox(coordinates, nil)
|
||||
bbox = &cbbox
|
||||
}
|
||||
return MultiPolygon{
|
||||
Coordinates: coordinates,
|
||||
BBox: bbox,
|
||||
bboxDefined: bboxDefined,
|
||||
}, err
|
||||
}
|
||||
|
||||
|
@ -58,12 +65,16 @@ func (g MultiPolygon) Weight() int {
|
|||
|
||||
// MarshalJSON allows the object to be encoded in json.Marshal calls.
|
||||
func (g MultiPolygon) MarshalJSON() ([]byte, error) {
|
||||
return []byte(g.JSON()), nil
|
||||
return g.appendJSON(nil), nil
|
||||
}
|
||||
|
||||
func (g MultiPolygon) appendJSON(json []byte) []byte {
|
||||
return appendLevel4JSON(json, "MultiPolygon", g.Coordinates, g.BBox, g.bboxDefined)
|
||||
}
|
||||
|
||||
// JSON is the json representation of the object. This might not be exactly the same as the original.
|
||||
func (g MultiPolygon) JSON() string {
|
||||
return level4JSON("MultiPolygon", g.Coordinates, g.BBox)
|
||||
return string(g.appendJSON(nil))
|
||||
}
|
||||
|
||||
// String returns a string representation of the object. This might be JSON or something else.
|
||||
|
@ -75,7 +86,7 @@ func (g MultiPolygon) bboxPtr() *BBox {
|
|||
return g.BBox
|
||||
}
|
||||
func (g MultiPolygon) hasPositions() bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return true
|
||||
}
|
||||
for _, c := range g.Coordinates {
|
||||
|
@ -90,7 +101,7 @@ func (g MultiPolygon) hasPositions() bool {
|
|||
|
||||
// WithinBBox detects if the object is fully contained inside a bbox.
|
||||
func (g MultiPolygon) WithinBBox(bbox BBox) bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox))
|
||||
}
|
||||
if len(g.Coordinates) == 0 {
|
||||
|
@ -192,7 +203,7 @@ func (g MultiPolygon) Nearby(center Position, meters float64) bool {
|
|||
|
||||
// IsBBoxDefined returns true if the object has a defined bbox.
|
||||
func (g MultiPolygon) IsBBoxDefined() bool {
|
||||
return g.BBox != nil
|
||||
return g.bboxDefined
|
||||
}
|
||||
|
||||
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
package geojson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/tile38/geojson/poly"
|
||||
|
@ -61,6 +58,8 @@ const nilz = 0
|
|||
type Object interface {
|
||||
bboxPtr() *BBox
|
||||
hasPositions() bool
|
||||
appendJSON(dst []byte) []byte
|
||||
|
||||
// WithinBBox detects if the object is fully contained inside a bbox.
|
||||
WithinBBox(bbox BBox) bool
|
||||
// IntersectsBBox detects if the object intersects a bbox.
|
||||
|
@ -93,39 +92,6 @@ type Object interface {
|
|||
IsGeometry() bool
|
||||
}
|
||||
|
||||
func writeHeader(buf *bytes.Buffer, objType byte, bbox *BBox, isCordZ bool) {
|
||||
header := objType
|
||||
if bbox != nil {
|
||||
header |= 1 << 4
|
||||
if bbox.Min.Z != nilz || bbox.Max.Z != nilz {
|
||||
header |= 1 << 5
|
||||
}
|
||||
}
|
||||
if isCordZ {
|
||||
header |= 1 << 6
|
||||
}
|
||||
buf.WriteByte(header)
|
||||
if bbox != nil {
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, math.Float64bits(bbox.Min.X))
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint64(b, math.Float64bits(bbox.Min.Y))
|
||||
buf.Write(b)
|
||||
if bbox.Min.Z != nilz || bbox.Max.Z != nilz {
|
||||
binary.LittleEndian.PutUint64(b, math.Float64bits(bbox.Min.Z))
|
||||
buf.Write(b)
|
||||
}
|
||||
binary.LittleEndian.PutUint64(b, math.Float64bits(bbox.Max.X))
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint64(b, math.Float64bits(bbox.Max.Y))
|
||||
buf.Write(b)
|
||||
if bbox.Min.Z != nilz || bbox.Max.Z != nilz {
|
||||
binary.LittleEndian.PutUint64(b, math.Float64bits(bbox.Max.Z))
|
||||
buf.Write(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func positionBBox(i int, bbox BBox, ps []Position) (int, BBox) {
|
||||
for _, p := range ps {
|
||||
if i == 0 {
|
||||
|
@ -219,7 +185,12 @@ func objectMap(json string, from int) (Object, error) {
|
|||
func withinObjectShared(g Object, o Object, pin func(v Polygon) bool, mpin func(v MultiPolygon) bool) bool {
|
||||
bbp := o.bboxPtr()
|
||||
if bbp != nil {
|
||||
return g.WithinBBox(*bbp)
|
||||
if !g.WithinBBox(*bbp) {
|
||||
return false
|
||||
}
|
||||
if o.IsBBoxDefined() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
switch v := o.(type) {
|
||||
default:
|
||||
|
@ -266,7 +237,12 @@ func withinObjectShared(g Object, o Object, pin func(v Polygon) bool, mpin func(
|
|||
func intersectsObjectShared(g Object, o Object, pin func(v Polygon) bool, mpin func(v MultiPolygon) bool) bool {
|
||||
bbp := o.bboxPtr()
|
||||
if bbp != nil {
|
||||
return g.IntersectsBBox(*bbp)
|
||||
if !g.IntersectsBBox(*bbp) {
|
||||
return false
|
||||
}
|
||||
if o.IsBBoxDefined() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
switch v := o.(type) {
|
||||
default:
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
type Point struct {
|
||||
Coordinates Position
|
||||
BBox *BBox
|
||||
bboxDefined bool
|
||||
}
|
||||
|
||||
func fillSimplePointOrPoint(coordinates Position, bbox *BBox, err error) (Object, error) {
|
||||
|
@ -20,9 +21,15 @@ func fillSimplePointOrPoint(coordinates Position, bbox *BBox, err error) (Object
|
|||
}
|
||||
|
||||
func fillPoint(coordinates Position, bbox *BBox, err error) (Point, error) {
|
||||
bboxDefined := bbox != nil
|
||||
if !bboxDefined {
|
||||
cbbox := level1CalculatedBBox(coordinates, nil)
|
||||
bbox = &cbbox
|
||||
}
|
||||
return Point{
|
||||
Coordinates: coordinates,
|
||||
BBox: bbox,
|
||||
bboxDefined: bboxDefined,
|
||||
}, err
|
||||
}
|
||||
|
||||
|
@ -47,12 +54,16 @@ func (g Point) Geohash(precision int) (string, error) {
|
|||
|
||||
// MarshalJSON allows the object to be encoded in json.Marshal calls.
|
||||
func (g Point) MarshalJSON() ([]byte, error) {
|
||||
return []byte(g.JSON()), nil
|
||||
return g.appendJSON(nil), nil
|
||||
}
|
||||
|
||||
func (g Point) appendJSON(json []byte) []byte {
|
||||
return appendLevel1JSON(json, "Point", g.Coordinates, g.BBox, g.bboxDefined)
|
||||
}
|
||||
|
||||
// JSON is the json representation of the object. This might not be exactly the same as the original.
|
||||
func (g Point) JSON() string {
|
||||
return level1JSON("Point", g.Coordinates, g.BBox)
|
||||
return string(g.appendJSON(nil))
|
||||
}
|
||||
|
||||
// String returns a string representation of the object. This might be JSON or something else.
|
||||
|
@ -78,7 +89,7 @@ func (g Point) hasPositions() bool {
|
|||
|
||||
// WithinBBox detects if the object is fully contained inside a bbox.
|
||||
func (g Point) WithinBBox(bbox BBox) bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox))
|
||||
}
|
||||
return poly.Point(g.Coordinates).InsideRect(rectBBox(bbox))
|
||||
|
@ -86,7 +97,7 @@ func (g Point) WithinBBox(bbox BBox) bool {
|
|||
|
||||
// IntersectsBBox detects if the object intersects a bbox.
|
||||
func (g Point) IntersectsBBox(bbox BBox) bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox))
|
||||
}
|
||||
return poly.Point(g.Coordinates).InsideRect(rectBBox(bbox))
|
||||
|
@ -133,7 +144,7 @@ func (g Point) Nearby(center Position, meters float64) bool {
|
|||
|
||||
// IsBBoxDefined returns true if the object has a defined bbox.
|
||||
func (g Point) IsBBoxDefined() bool {
|
||||
return g.BBox != nil
|
||||
return g.bboxDefined
|
||||
}
|
||||
|
||||
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
package geojson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
|
||||
"github.com/tidwall/tile38/geojson/geohash"
|
||||
)
|
||||
import "github.com/tidwall/tile38/geojson/geohash"
|
||||
|
||||
// Polygon is a geojson object with the type "Polygon"
|
||||
type Polygon struct {
|
||||
Coordinates [][]Position
|
||||
BBox *BBox
|
||||
bboxDefined bool
|
||||
}
|
||||
|
||||
func fillPolygon(coordinates [][]Position, bbox *BBox, err error) (Polygon, error) {
|
||||
|
@ -27,9 +23,15 @@ func fillPolygon(coordinates [][]Position, bbox *BBox, err error) (Polygon, erro
|
|||
}
|
||||
}
|
||||
}
|
||||
bboxDefined := bbox != nil
|
||||
if !bboxDefined {
|
||||
cbbox := level3CalculatedBBox(coordinates, nil, true)
|
||||
bbox = &cbbox
|
||||
}
|
||||
return Polygon{
|
||||
Coordinates: coordinates,
|
||||
BBox: bbox,
|
||||
bboxDefined: bboxDefined,
|
||||
}, err
|
||||
}
|
||||
|
||||
|
@ -61,12 +63,16 @@ func (g Polygon) Weight() int {
|
|||
|
||||
// MarshalJSON allows the object to be encoded in json.Marshal calls.
|
||||
func (g Polygon) MarshalJSON() ([]byte, error) {
|
||||
return []byte(g.JSON()), nil
|
||||
return g.appendJSON(nil), nil
|
||||
}
|
||||
|
||||
func (g Polygon) appendJSON(json []byte) []byte {
|
||||
return appendLevel3JSON(json, "Polygon", g.Coordinates, g.BBox, g.bboxDefined)
|
||||
}
|
||||
|
||||
// JSON is the json representation of the object. This might not be exactly the same as the original.
|
||||
func (g Polygon) JSON() string {
|
||||
return level3JSON("Polygon", g.Coordinates, g.BBox)
|
||||
return string(g.appendJSON(nil))
|
||||
}
|
||||
|
||||
// String returns a string representation of the object. This might be JSON or something else.
|
||||
|
@ -78,7 +84,7 @@ func (g Polygon) bboxPtr() *BBox {
|
|||
return g.BBox
|
||||
}
|
||||
func (g Polygon) hasPositions() bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return true
|
||||
}
|
||||
for _, c := range g.Coordinates {
|
||||
|
@ -91,7 +97,7 @@ func (g Polygon) hasPositions() bool {
|
|||
|
||||
// WithinBBox detects if the object is fully contained inside a bbox.
|
||||
func (g Polygon) WithinBBox(bbox BBox) bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox))
|
||||
}
|
||||
if len(g.Coordinates) == 0 {
|
||||
|
@ -110,7 +116,7 @@ func (g Polygon) WithinBBox(bbox BBox) bool {
|
|||
|
||||
// IntersectsBBox detects if the object intersects a bbox.
|
||||
func (g Polygon) IntersectsBBox(bbox BBox) bool {
|
||||
if g.BBox != nil {
|
||||
if g.bboxDefined {
|
||||
return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox))
|
||||
}
|
||||
if len(g.Coordinates) == 0 {
|
||||
|
@ -178,46 +184,9 @@ func (g Polygon) Nearby(center Position, meters float64) bool {
|
|||
return nearbyObjectShared(g, center.X, center.Y, meters)
|
||||
}
|
||||
|
||||
// KML outputs kml
|
||||
func (g Polygon) KML() string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(`<?xml version="1.0" encoding="UTF-8"?>`)
|
||||
buf.WriteString(`<kml xmlns="http://www.opengis.net/kml/2.2">`)
|
||||
buf.WriteString(`<Placemark>`)
|
||||
buf.WriteString(`<Polygon>`)
|
||||
buf.WriteString(`<extrude>1</extrude>`)
|
||||
buf.WriteString(`<altitudeMode>relativeToGround</altitudeMode>`)
|
||||
for i, c := range g.Coordinates {
|
||||
if i == 0 {
|
||||
buf.WriteString(`<outerBoundaryIs>`)
|
||||
} else {
|
||||
buf.WriteString(`<innerBoundaryIs>`)
|
||||
}
|
||||
buf.WriteString(`<LinearRing>`)
|
||||
buf.WriteString(`<coordinates>`)
|
||||
for _, c := range c {
|
||||
buf.WriteString("\n" + strconv.FormatFloat(c.X, 'f', -1, 64) + `,` + strconv.FormatFloat(c.Y, 'f', -1, 64) + `,` + strconv.FormatFloat(c.Z, 'f', -1, 64))
|
||||
}
|
||||
if len(c) > 0 {
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
buf.WriteString(`</coordinates>`)
|
||||
buf.WriteString(`</LinearRing>`)
|
||||
if i == 0 {
|
||||
buf.WriteString(`</outerBoundaryIs>`)
|
||||
} else {
|
||||
buf.WriteString(`</innerBoundaryIs>`)
|
||||
}
|
||||
}
|
||||
buf.WriteString(`</Polygon>`)
|
||||
buf.WriteString(`</Placemark>`)
|
||||
buf.WriteString(`</kml>`)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// IsBBoxDefined returns true if the object has a defined bbox.
|
||||
func (g Polygon) IsBBoxDefined() bool {
|
||||
return g.BBox != nil
|
||||
return g.bboxDefined
|
||||
}
|
||||
|
||||
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package geojson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"strconv"
|
||||
|
@ -38,26 +37,15 @@ func polyExteriorHoles(positions [][]Position) (exterior poly.Polygon, holes []p
|
|||
return
|
||||
}
|
||||
|
||||
func (p Position) writeJSON(buf *bytes.Buffer, isCordZ bool) {
|
||||
buf.WriteString(strconv.FormatFloat(p.X, 'f', -1, 64))
|
||||
buf.WriteByte(',')
|
||||
buf.WriteString(strconv.FormatFloat(p.Y, 'f', -1, 64))
|
||||
func appendPositionJSON(json []byte, p Position, isCordZ bool) []byte {
|
||||
json = strconv.AppendFloat(json, p.X, 'f', -1, 64)
|
||||
json = append(json, ',')
|
||||
json = strconv.AppendFloat(json, p.Y, 'f', -1, 64)
|
||||
if isCordZ {
|
||||
buf.WriteByte(',')
|
||||
buf.WriteString(strconv.FormatFloat(p.Z, 'f', -1, 64))
|
||||
}
|
||||
}
|
||||
|
||||
func (p Position) writeBytes(buf *bytes.Buffer, isCordZ bool) {
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, math.Float64bits(p.X))
|
||||
buf.Write(b)
|
||||
binary.LittleEndian.PutUint64(b, math.Float64bits(p.Y))
|
||||
buf.Write(b)
|
||||
if isCordZ {
|
||||
binary.LittleEndian.PutUint64(b, math.Float64bits(p.Z))
|
||||
buf.Write(b)
|
||||
json = append(json, ',')
|
||||
json = strconv.AppendFloat(json, p.Z, 'f', -1, 64)
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
||||
const earthRadius = 6371e3
|
||||
|
|
|
@ -51,12 +51,16 @@ func (g SimplePoint) Weight() int {
|
|||
|
||||
// MarshalJSON allows the object to be encoded in json.Marshal calls.
|
||||
func (g SimplePoint) MarshalJSON() ([]byte, error) {
|
||||
return []byte(g.JSON()), nil
|
||||
return g.appendJSON(nil), nil
|
||||
}
|
||||
|
||||
func (g SimplePoint) appendJSON(json []byte) []byte {
|
||||
return appendLevel1JSON(json, "Point", Position{X: g.X, Y: g.Y, Z: 0}, nil, false)
|
||||
}
|
||||
|
||||
// JSON is the json representation of the object. This might not be exactly the same as the original.
|
||||
func (g SimplePoint) JSON() string {
|
||||
return level1JSON("Point", Position{X: g.X, Y: g.Y, Z: 0}, nil)
|
||||
return string(g.appendJSON(nil))
|
||||
}
|
||||
|
||||
// String returns a string representation of the object. This might be JSON or something else.
|
||||
|
|
|
@ -45,10 +45,14 @@ func (s String) CalculatedPoint() Position {
|
|||
return Position{}
|
||||
}
|
||||
|
||||
func (s String) appendJSON(json []byte) []byte {
|
||||
b, _ := s.MarshalJSON()
|
||||
return append(json, b...)
|
||||
}
|
||||
|
||||
// JSON is the json representation of the object. This might not be exactly the same as the original.
|
||||
func (s String) JSON() string {
|
||||
b, _ := s.MarshalJSON()
|
||||
return string(b)
|
||||
return string(s.appendJSON(nil))
|
||||
}
|
||||
|
||||
// String returns a string representation of the object. This might be JSON or something else.
|
||||
|
@ -57,7 +61,7 @@ func (s String) String() string {
|
|||
}
|
||||
|
||||
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
|
||||
func (g String) IsGeometry() bool {
|
||||
func (s String) IsGeometry() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue