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