1package geojson 2 3import "github.com/tidwall/tile38/geojson/geohash" 4 5// Polygon is a geojson object with the type "Polygon" 6type Polygon struct { 7 Coordinates [][]Position 8 BBox *BBox 9 bboxDefined bool 10} 11 12func fillPolygon(coordinates [][]Position, bbox *BBox, err error) (Polygon, error) { 13 if err == nil { 14 if len(coordinates) == 0 { 15 err = errMustBeALinearRing 16 } 17 } 18 if err == nil { 19 for _, ps := range coordinates { 20 if !isLinearRing(ps) { 21 err = errMustBeALinearRing 22 break 23 } 24 } 25 } 26 bboxDefined := bbox != nil 27 if !bboxDefined { 28 cbbox := level3CalculatedBBox(coordinates, nil, true) 29 bbox = &cbbox 30 } 31 return Polygon{ 32 Coordinates: coordinates, 33 BBox: bbox, 34 bboxDefined: bboxDefined, 35 }, err 36} 37 38// CalculatedBBox is exterior bbox containing the object. 39func (g Polygon) CalculatedBBox() BBox { 40 return level3CalculatedBBox(g.Coordinates, g.BBox, true) 41} 42 43// CalculatedPoint is a point representation of the object. 44func (g Polygon) CalculatedPoint() Position { 45 return g.CalculatedBBox().center() 46} 47 48// Geohash converts the object to a geohash value. 49func (g Polygon) Geohash(precision int) (string, error) { 50 p := g.CalculatedPoint() 51 return geohash.Encode(p.Y, p.X, precision) 52} 53 54// PositionCount return the number of coordinates. 55func (g Polygon) PositionCount() int { 56 return level3PositionCount(g.Coordinates, g.BBox) 57} 58 59// Weight returns the in-memory size of the object. 60func (g Polygon) Weight() int { 61 return level3Weight(g.Coordinates, g.BBox) 62} 63 64// MarshalJSON allows the object to be encoded in json.Marshal calls. 65func (g Polygon) MarshalJSON() ([]byte, error) { 66 return g.appendJSON(nil), nil 67} 68 69func (g Polygon) appendJSON(json []byte) []byte { 70 return appendLevel3JSON(json, "Polygon", g.Coordinates, g.BBox, g.bboxDefined) 71} 72 73// JSON is the json representation of the object. This might not be exactly the same as the original. 74func (g Polygon) JSON() string { 75 return string(g.appendJSON(nil)) 76} 77 78// String returns a string representation of the object. This might be JSON or something else. 79func (g Polygon) String() string { 80 return g.JSON() 81} 82 83func (g Polygon) bboxPtr() *BBox { 84 return g.BBox 85} 86func (g Polygon) hasPositions() bool { 87 if g.bboxDefined { 88 return true 89 } 90 for _, c := range g.Coordinates { 91 if len(c) > 0 { 92 return true 93 } 94 } 95 return false 96} 97 98// WithinBBox detects if the object is fully contained inside a bbox. 99func (g Polygon) WithinBBox(bbox BBox) bool { 100 if g.bboxDefined { 101 return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox)) 102 } 103 if len(g.Coordinates) == 0 { 104 return false 105 } 106 if g.BBox != nil { 107 if !rectBBox(*g.BBox).IntersectsRect(rectBBox(bbox)) { 108 return false 109 } 110 } 111 rbbox := rectBBox(bbox) 112 ext, holes := polyExteriorHoles(g.Coordinates) 113 if len(holes) > 0 { 114 if rbbox.Max == rbbox.Min { 115 return rbbox.Min.Inside(ext, holes) 116 } 117 return rbbox.Inside(ext, holes) 118 } 119 return ext.InsideRect(rectBBox(bbox)) 120} 121 122// IntersectsBBox detects if the object intersects a bbox. 123func (g Polygon) IntersectsBBox(bbox BBox) bool { 124 if g.bboxDefined { 125 return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox)) 126 } 127 if len(g.Coordinates) == 0 { 128 return false 129 } 130 if g.BBox != nil { 131 if !rectBBox(*g.BBox).IntersectsRect(rectBBox(bbox)) { 132 return false 133 } 134 } 135 rbbox := rectBBox(bbox) 136 ext, holes := polyExteriorHoles(g.Coordinates) 137 if len(holes) > 0 { 138 if rbbox.Max == rbbox.Min { 139 return rbbox.Min.Intersects(ext, holes) 140 } 141 return rbbox.Intersects(ext, holes) 142 } 143 return ext.IntersectsRect(rectBBox(bbox)) 144} 145 146// Within detects if the object is fully contained inside another object. 147func (g Polygon) Within(o Object) bool { 148 return withinObjectShared(g, o, 149 func(v Polygon) bool { 150 if len(g.Coordinates) == 0 { 151 return false 152 } 153 return polyPositions(g.Coordinates[0]).Inside(polyExteriorHoles(v.Coordinates)) 154 }, 155 ) 156} 157 158// Intersects detects if the object intersects another object. 159func (g Polygon) Intersects(o Object) bool { 160 return intersectsObjectShared(g, o, 161 func(v Polygon) bool { 162 if len(g.Coordinates) == 0 { 163 return false 164 } 165 return polyPositions(g.Coordinates[0]).Intersects(polyExteriorHoles(v.Coordinates)) 166 }, 167 ) 168} 169 170// Nearby detects if the object is nearby a position. 171func (g Polygon) Nearby(center Position, meters float64) bool { 172 return nearbyObjectShared(g, center.X, center.Y, meters) 173} 174 175// IsBBoxDefined returns true if the object has a defined bbox. 176func (g Polygon) IsBBoxDefined() bool { 177 return g.bboxDefined 178} 179 180// IsGeometry return true if the object is a geojson geometry object. false if it something else. 181func (g Polygon) IsGeometry() bool { 182 return true 183} 184