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"
|
||||
@ -12,6 +11,7 @@ import (
|
||||
type Feature struct {
|
||||
Geometry Object
|
||||
BBox *BBox
|
||||
bboxDefined bool
|
||||
idprops string // raw id and properties seperated by a '\0'
|
||||
}
|
||||
|
||||
@ -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,8 +1,6 @@
|
||||
package geojson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/tile38/geojson/geohash"
|
||||
)
|
||||
@ -11,6 +9,7 @@ import (
|
||||
type FeatureCollection struct {
|
||||
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,8 +1,6 @@
|
||||
package geojson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/tile38/geojson/geohash"
|
||||
)
|
||||
@ -11,6 +9,7 @@ import (
|
||||
type GeometryCollection struct {
|
||||
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…
x
Reference in New Issue
Block a user