fixed dependencies
This commit is contained in:
176
vendor/github.com/paulmach/orb/CHANGELOG.md
generated
vendored
Normal file
176
vendor/github.com/paulmach/orb/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [v0.11.1](https://github.com/paulmach/orb/compare/v0.11.0...v0.11.1) - 2024-01-29
|
||||
|
||||
### Fixed
|
||||
|
||||
- geojson: `null` json into non-pointer Feature/FeatureCollection will set them to empty by [@paulmach](https://github.com/paulmach)in https://github.com/paulmach/orb/pull/145
|
||||
|
||||
## [v0.11.0](https://github.com/paulmach/orb/compare/v0.10.0...v0.11.0) - 2024-01-11
|
||||
|
||||
### Fixed
|
||||
|
||||
- quadtree: InBoundMatching does not properly accept passed-in buffer by [@nirmal-vuppuluri](https://github.com/nirmal-vuppuluri) in https://github.com/paulmach/orb/pull/139
|
||||
- mvt: Do not swallow error cause by [@m-pavel](https://github.com/m-pavel) in https://github.com/paulmach/orb/pull/137
|
||||
|
||||
### Changed
|
||||
|
||||
- simplify: Visvalingam, by default, keeps 3 points for "areas" by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/140
|
||||
- encoding/mvt: skip encoding of features will nil geometry by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/141
|
||||
- encoding/wkt: improve unmarshalling performance by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/142
|
||||
|
||||
## [v0.10.0](https://github.com/paulmach/orb/compare/v0.9.2...v0.10.0) - 2023-07-16
|
||||
|
||||
### Added
|
||||
|
||||
- add ChildrenInZoomRange method to maptile.Tile by [@peitili](https://github.com/peitili) in https://github.com/paulmach/orb/pull/133
|
||||
|
||||
## [v0.9.2](https://github.com/paulmach/orb/compare/v0.9.1...v0.9.2) - 2023-05-04
|
||||
|
||||
### Fixed
|
||||
|
||||
- encoding/wkt: better handling/validation of missing parens by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/131
|
||||
|
||||
## [v0.9.1](https://github.com/paulmach/orb/compare/v0.9.0...v0.9.1) - 2023-04-26
|
||||
|
||||
### Fixed
|
||||
|
||||
- Bump up mongo driver to 1.11.4 by [@m-pavel](https://github.com/m-pavel) in https://github.com/paulmach/orb/pull/129
|
||||
- encoding/wkt: split strings with regexp by [@m-pavel](https://github.com/m-pavel) in https://github.com/paulmach/orb/pull/128
|
||||
|
||||
## [v0.9.0](https://github.com/paulmach/orb/compare/v0.8.0...v0.9.0) - 2023-02-19
|
||||
|
||||
### Added
|
||||
|
||||
- geojson: marshal/unmarshal BSON [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/123
|
||||
|
||||
## [v0.8.0](https://github.com/paulmach/orb/compare/v0.7.1...v0.8.0) - 2023-01-05
|
||||
|
||||
### Fixed
|
||||
|
||||
- quadtree: fix bad sort due to pointer allocation issue by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/115
|
||||
- geojson: ensure geometry unmarshal errors get returned by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/117
|
||||
- encoding/mvt: remove use of crypto/md5 to compare marshalling in tests by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/118
|
||||
- encoding/wkt: fix panic for some invalid wkt data by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/119
|
||||
|
||||
### Other
|
||||
|
||||
- fix typo by [@rubenpoppe](https://github.com/rubenpoppe) in https://github.com/paulmach/orb/pull/107
|
||||
- Fixed a small twister in README.md by [@Timahawk](https://github.com/Timahawk) in https://github.com/paulmach/orb/pull/108
|
||||
- update github ci to use go 1.19 by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/116
|
||||
|
||||
## [v0.7.1](https://github.com/paulmach/orb/compare/v0.7.0...v0.7.1) - 2022-05-16
|
||||
|
||||
No changes
|
||||
|
||||
The v0.7.0 tag was updated since it initially pointed to the wrong commit. This is causing caching issues.
|
||||
|
||||
## [v0.7.0](https://github.com/paulmach/orb/compare/v0.6.0...v0.7.0) - 2022-05-10
|
||||
|
||||
This tag is broken, please use v0.7.1 instead.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- tilecover now returns an error (vs. panicing) on non-closed 2d geometry by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/87
|
||||
|
||||
This changes the signature of many of the methods in the [maptile/tilecover](https://github.com/paulmach/orb/tree/master/maptile/tilecover) package.
|
||||
To emulate the old behavior replace:
|
||||
|
||||
tiles := tilecover.Geometry(poly, zoom)
|
||||
|
||||
with
|
||||
|
||||
tiles, err := tilecover.Geometry(poly, zoom)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
## [v0.6.0](https://github.com/paulmach/orb/compare/v0.5.0...v0.6.0) - 2022-05-04
|
||||
|
||||
### Added
|
||||
|
||||
- geo: add correctly spelled LengthHaversine by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/97
|
||||
- geojson: add support for "external" json encoders/decoders by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/98
|
||||
- Add ewkb encoding/decoding support by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/88
|
||||
|
||||
## [v0.5.0](https://github.com/paulmach/orb/compare/v0.4.0...v0.5.0) - 2022-04-06
|
||||
|
||||
### Added
|
||||
|
||||
- encoding/mvt: stable marshalling by [@travisgrigsby](https://github.com/travisgrigsby) in https://github.com/paulmach/orb/pull/93
|
||||
- encoding/mvt: support mvt marshal for GeometryCollection by [@dadadamarine](https://github.com/dadadamarine) in https://github.com/paulmach/orb/pull/89
|
||||
|
||||
### Fixed
|
||||
|
||||
- quadtree: fix cleanup of nodes during removal by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/94
|
||||
|
||||
### Other
|
||||
|
||||
- encoding/wkt: various code improvements by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/95
|
||||
- update protoscan to 0.2.1 by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/83
|
||||
|
||||
## [v0.4.0](https://github.com/paulmach/orb/compare/v0.3.0...v0.4.0) - 2021-11-11
|
||||
|
||||
### Added
|
||||
|
||||
- geo: Add functions to calculate points based on distance and bearing by [@thzinc](https://github.com/thzinc) in https://github.com/paulmach/orb/pull/76
|
||||
|
||||
### Fixed
|
||||
|
||||
- encoding/mvt: avoid reflect nil value by [@nicklasaven](https://github.com/nicklasaven) in https://github.com/paulmach/orb/pull/78
|
||||
|
||||
## [v0.3.0](https://github.com/paulmach/orb/compare/v0.2.2...v0.3.0) - 2021-10-16
|
||||
|
||||
### Changed
|
||||
|
||||
- quadtree: sort KNearest results closest first by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/75
|
||||
- ring: require >=4 points to return true when calling Closed() by [@missinglink](https://github.com/missinglink) in https://github.com/paulmach/orb/pull/70
|
||||
|
||||
### Fixed
|
||||
|
||||
- encoding/mvt: verify tile coord does not overflow for z > 20 by [@paulmach](https://github.com/paulmach) in https://github.com/paulmach/orb/pull/74
|
||||
- quadtree: Address panic-ing quadtree.Matching(…) method when finding no closest node by [@willsalz](https://github.com/willsalz) in https://github.com/paulmach/orb/pull/73
|
||||
|
||||
## [v0.2.2](https://github.com/paulmach/orb/compare/v0.2.1...v0.2.2) - 2021-06-05
|
||||
|
||||
### Fixed
|
||||
|
||||
- Dependency resolution problems in some cases, issue https://github.com/paulmach/orb/issues/65, pr https://github.com/paulmach/orb/pull/66
|
||||
|
||||
## [v0.2.1](https://github.com/paulmach/orb/compare/v0.2.0...v0.2.1) - 2021-01-16
|
||||
|
||||
### Changed
|
||||
|
||||
- encoding/mvt: upgrade protoscan v0.1 -> v0.2 [`ad31566`](https://github.com/paulmach/orb/commit/ad31566942027c1cd30dd341f35123fb54676599)
|
||||
- encoding/mvt: remove github.com/pkg/errors as a dependency [`d2e235`](https://github.com/paulmach/orb/commit/d2e23529a295a0d973cc787ad2742cb6ccbd5306)
|
||||
|
||||
## v0.2.0 - 2021-01-16
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Foreign Members in Feature Collections
|
||||
|
||||
Extra attributes in a feature collection object will now be put into `featureCollection.ExtraMembers`.
|
||||
Similarly, stuff in `ExtraMembers will be marshalled into the feature collection base.
|
||||
The break happens if you were decoding these foreign members using something like
|
||||
|
||||
```go
|
||||
type MyFeatureCollection struct {
|
||||
geojson.FeatureCollection
|
||||
Title string `json:"title"`
|
||||
}
|
||||
```
|
||||
|
||||
**The above will no longer work** in this release and it never supported marshalling. See https://github.com/paulmach/orb/pull/56 for more details.
|
||||
|
||||
- Features with nil/missing geometry will no longer return an errors
|
||||
|
||||
Previously missing or invalid geometry in a feature collection would return a `ErrInvalidGeometry` error.
|
||||
However missing geometry is compliant with [section 3.2](https://tools.ietf.org/html/rfc7946#section-3.2) of the spec.
|
||||
See https://github.com/paulmach/orb/issues/38 and https://github.com/paulmach/orb/pull/58 for more details.
|
||||
|
||||
### Changed
|
||||
|
||||
- encoding/mvt: faster unmarshalling for Mapbox Vector Tiles (MVT) see https://github.com/paulmach/orb/pull/57
|
||||
20
vendor/github.com/paulmach/orb/LICENSE.md
generated
vendored
Normal file
20
vendor/github.com/paulmach/orb/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Paul Mach
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
181
vendor/github.com/paulmach/orb/README.md
generated
vendored
Normal file
181
vendor/github.com/paulmach/orb/README.md
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
# orb [](https://github.com/paulmach/orb/actions?query=workflow%3ACI+event%3Apush) [](https://codecov.io/gh/paulmach/orb) [](https://goreportcard.com/report/github.com/paulmach/orb) [](https://pkg.go.dev/github.com/paulmach/orb)
|
||||
|
||||
Package `orb` defines a set of types for working with 2d geo and planar/projected geometric data in Golang.
|
||||
There are a set of sub-packages that use these types to do interesting things.
|
||||
They each provide their own README with extra info.
|
||||
|
||||
## Interesting features
|
||||
|
||||
- **Simple types** - allow for natural operations using the `make`, `append`, `len`, `[s:e]` builtins.
|
||||
- **GeoJSON** - support as part of the [`geojson`](geojson) sub-package.
|
||||
- **Mapbox Vector Tile** - encoding and decoding as part of the [`encoding/mvt`](encoding/mvt) sub-package.
|
||||
- **Direct to type from DB query results** - by scanning WKB data directly into types.
|
||||
- **Rich set of sub-packages** - including [`clipping`](clip), [`simplifing`](simplify), [`quadtree`](quadtree) and more.
|
||||
|
||||
## Type definitions
|
||||
|
||||
```go
|
||||
type Point [2]float64
|
||||
type MultiPoint []Point
|
||||
|
||||
type LineString []Point
|
||||
type MultiLineString []LineString
|
||||
|
||||
type Ring LineString
|
||||
type Polygon []Ring
|
||||
type MultiPolygon []Polygon
|
||||
|
||||
type Collection []Geometry
|
||||
|
||||
type Bound struct { Min, Max Point }
|
||||
```
|
||||
|
||||
Defining the types as slices allows them to be accessed in an idiomatic way
|
||||
using Go's built-in functions such at `make`, `append`, `len`
|
||||
and with slice notation like `[s:e]`. For example:
|
||||
|
||||
```go
|
||||
ls := make(orb.LineString, 0, 100)
|
||||
ls = append(ls, orb.Point{1, 1})
|
||||
point := ls[0]
|
||||
```
|
||||
|
||||
### Shared `Geometry` interface
|
||||
|
||||
All of the base types implement the `orb.Geometry` interface defined as:
|
||||
|
||||
```go
|
||||
type Geometry interface {
|
||||
GeoJSONType() string
|
||||
Dimensions() int // e.g. 0d, 1d, 2d
|
||||
Bound() Bound
|
||||
}
|
||||
```
|
||||
|
||||
This interface is accepted by functions in the sub-packages which then act on the
|
||||
base types correctly. For example:
|
||||
|
||||
```go
|
||||
l := clip.Geometry(bound, geom)
|
||||
```
|
||||
|
||||
will use the appropriate clipping algorithm depending on if the input is 1d or 2d,
|
||||
e.g. a `orb.LineString` or a `orb.Polygon`.
|
||||
|
||||
Only a few methods are defined directly on these type, for example `Clone`, `Equal`, `GeoJSONType`.
|
||||
Other operation that depend on geo vs. planar contexts are defined in the respective sub-package.
|
||||
For example:
|
||||
|
||||
- Computing the geo distance between two point:
|
||||
|
||||
```go
|
||||
p1 := orb.Point{-72.796408, -45.407131}
|
||||
p2 := orb.Point{-72.688541, -45.384987}
|
||||
|
||||
geo.Distance(p1, p2)
|
||||
```
|
||||
|
||||
- Compute the planar area and centroid of a polygon:
|
||||
|
||||
```go
|
||||
poly := orb.Polygon{...}
|
||||
centroid, area := planar.CentroidArea(poly)
|
||||
```
|
||||
|
||||
## GeoJSON
|
||||
|
||||
The [geojson](geojson) sub-package implements Marshalling and Unmarshalling of GeoJSON data.
|
||||
Features are defined as:
|
||||
|
||||
```go
|
||||
type Feature struct {
|
||||
ID interface{} `json:"id,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Geometry orb.Geometry `json:"geometry"`
|
||||
Properties Properties `json:"properties"`
|
||||
}
|
||||
```
|
||||
|
||||
Defining the geometry as an `orb.Geometry` interface along with sub-package functions
|
||||
accepting geometries allows them to work together to create easy to follow code.
|
||||
For example, clipping all the geometries in a collection:
|
||||
|
||||
```go
|
||||
fc, err := geojson.UnmarshalFeatureCollection(data)
|
||||
for _, f := range fc {
|
||||
f.Geometry = clip.Geometry(bound, f.Geometry)
|
||||
}
|
||||
```
|
||||
|
||||
The library supports third party "encoding/json" replacements
|
||||
such [github.com/json-iterator/go](https://github.com/json-iterator/go).
|
||||
See the [geojson](geojson) readme for more details.
|
||||
|
||||
The types also support BSON so they can be used directly when working with MongoDB.
|
||||
|
||||
## Mapbox Vector Tiles
|
||||
|
||||
The [encoding/mvt](encoding/mvt) sub-package implements Marshalling and
|
||||
Unmarshalling [MVT](https://www.mapbox.com/vector-tiles/) data.
|
||||
This package uses sets of `geojson.FeatureCollection` to define the layers,
|
||||
keyed by the layer name. For example:
|
||||
|
||||
```go
|
||||
collections := map[string]*geojson.FeatureCollection{}
|
||||
|
||||
// Convert to a layers object and project to tile coordinates.
|
||||
layers := mvt.NewLayers(collections)
|
||||
layers.ProjectToTile(maptile.New(x, y, z))
|
||||
|
||||
// In order to be used as source for MapboxGL geometries need to be clipped
|
||||
// to max allowed extent. (uncomment next line)
|
||||
// layers.Clip(mvt.MapboxGLDefaultExtentBound)
|
||||
|
||||
// Simplify the geometry now that it's in tile coordinate space.
|
||||
layers.Simplify(simplify.DouglasPeucker(1.0))
|
||||
|
||||
// Depending on use-case remove empty geometry, those too small to be
|
||||
// represented in this tile space.
|
||||
// In this case lines shorter than 1, and areas smaller than 2.
|
||||
layers.RemoveEmpty(1.0, 2.0)
|
||||
|
||||
// encoding using the Mapbox Vector Tile protobuf encoding.
|
||||
data, err := mvt.Marshal(layers) // this data is NOT gzipped.
|
||||
|
||||
// Sometimes MVT data is stored and transfered gzip compressed. In that case:
|
||||
data, err := mvt.MarshalGzipped(layers)
|
||||
```
|
||||
|
||||
## Decoding WKB/EWKB from a database query
|
||||
|
||||
Geometries are usually returned from databases in WKB or EWKB format. The [encoding/ewkb](encoding/ewkb)
|
||||
sub-package offers helpers to "scan" the data into the base types directly.
|
||||
For example:
|
||||
|
||||
```go
|
||||
db.Exec(
|
||||
"INSERT INTO postgis_table (point_column) VALUES (ST_GeomFromEWKB(?))",
|
||||
ewkb.Value(orb.Point{1, 2}, 4326),
|
||||
)
|
||||
|
||||
row := db.QueryRow("SELECT ST_AsBinary(point_column) FROM postgis_table")
|
||||
|
||||
var p orb.Point
|
||||
err := row.Scan(ewkb.Scanner(&p))
|
||||
```
|
||||
|
||||
For more information see the readme in the [encoding/ewkb](encoding/ewkb) package.
|
||||
|
||||
## List of sub-package utilities
|
||||
|
||||
- [`clip`](clip) - clipping geometry to a bounding box
|
||||
- [`encoding/mvt`](encoding/mvt) - encoded and decoding from [Mapbox Vector Tiles](https://www.mapbox.com/vector-tiles/)
|
||||
- [`encoding/wkb`](encoding/wkb) - well-known binary as well as helpers to decode from the database queries
|
||||
- [`encoding/ewkb`](encoding/ewkb) - extended well-known binary format that includes the SRID
|
||||
- [`encoding/wkt`](encoding/wkt) - well-known text encoding
|
||||
- [`geojson`](geojson) - working with geojson and the types in this package
|
||||
- [`maptile`](maptile) - working with mercator map tiles and quadkeys
|
||||
- [`project`](project) - project geometries between geo and planar contexts
|
||||
- [`quadtree`](quadtree) - quadtree implementation using the types in this package
|
||||
- [`resample`](resample) - resample points in a line string geometry
|
||||
- [`simplify`](simplify) - linear geometry simplifications like Douglas-Peucker
|
||||
172
vendor/github.com/paulmach/orb/bound.go
generated
vendored
Normal file
172
vendor/github.com/paulmach/orb/bound.go
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
package orb
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
var emptyBound = Bound{Min: Point{1, 1}, Max: Point{-1, -1}}
|
||||
|
||||
// A Bound represents a closed box or rectangle.
|
||||
// To create a bound with two points you can do something like:
|
||||
// orb.MultiPoint{p1, p2}.Bound()
|
||||
type Bound struct {
|
||||
Min, Max Point
|
||||
}
|
||||
|
||||
// GeoJSONType returns the GeoJSON type for the object.
|
||||
func (b Bound) GeoJSONType() string {
|
||||
return "Polygon"
|
||||
}
|
||||
|
||||
// Dimensions returns 2 because a Bound is a 2d object.
|
||||
func (b Bound) Dimensions() int {
|
||||
return 2
|
||||
}
|
||||
|
||||
// ToPolygon converts the bound into a Polygon object.
|
||||
func (b Bound) ToPolygon() Polygon {
|
||||
return Polygon{b.ToRing()}
|
||||
}
|
||||
|
||||
// ToRing converts the bound into a loop defined
|
||||
// by the boundary of the box.
|
||||
func (b Bound) ToRing() Ring {
|
||||
return Ring{
|
||||
b.Min,
|
||||
Point{b.Max[0], b.Min[1]},
|
||||
b.Max,
|
||||
Point{b.Min[0], b.Max[1]},
|
||||
b.Min,
|
||||
}
|
||||
}
|
||||
|
||||
// Extend grows the bound to include the new point.
|
||||
func (b Bound) Extend(point Point) Bound {
|
||||
// already included, no big deal
|
||||
if b.Contains(point) {
|
||||
return b
|
||||
}
|
||||
|
||||
return Bound{
|
||||
Min: Point{
|
||||
math.Min(b.Min[0], point[0]),
|
||||
math.Min(b.Min[1], point[1]),
|
||||
},
|
||||
Max: Point{
|
||||
math.Max(b.Max[0], point[0]),
|
||||
math.Max(b.Max[1], point[1]),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Union extends this bound to contain the union of this and the given bound.
|
||||
func (b Bound) Union(other Bound) Bound {
|
||||
if other.IsEmpty() {
|
||||
return b
|
||||
}
|
||||
|
||||
b = b.Extend(other.Min)
|
||||
b = b.Extend(other.Max)
|
||||
b = b.Extend(other.LeftTop())
|
||||
b = b.Extend(other.RightBottom())
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Contains determines if the point is within the bound.
|
||||
// Points on the boundary are considered within.
|
||||
func (b Bound) Contains(point Point) bool {
|
||||
if point[1] < b.Min[1] || b.Max[1] < point[1] {
|
||||
return false
|
||||
}
|
||||
|
||||
if point[0] < b.Min[0] || b.Max[0] < point[0] {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Intersects determines if two bounds intersect.
|
||||
// Returns true if they are touching.
|
||||
func (b Bound) Intersects(bound Bound) bool {
|
||||
if (b.Max[0] < bound.Min[0]) ||
|
||||
(b.Min[0] > bound.Max[0]) ||
|
||||
(b.Max[1] < bound.Min[1]) ||
|
||||
(b.Min[1] > bound.Max[1]) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Pad extends the bound in all directions by the given value.
|
||||
func (b Bound) Pad(d float64) Bound {
|
||||
b.Min[0] -= d
|
||||
b.Min[1] -= d
|
||||
|
||||
b.Max[0] += d
|
||||
b.Max[1] += d
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Center returns the center of the bounds by "averaging" the x and y coords.
|
||||
func (b Bound) Center() Point {
|
||||
return Point{
|
||||
(b.Min[0] + b.Max[0]) / 2.0,
|
||||
(b.Min[1] + b.Max[1]) / 2.0,
|
||||
}
|
||||
}
|
||||
|
||||
// Top returns the top of the bound.
|
||||
func (b Bound) Top() float64 {
|
||||
return b.Max[1]
|
||||
}
|
||||
|
||||
// Bottom returns the bottom of the bound.
|
||||
func (b Bound) Bottom() float64 {
|
||||
return b.Min[1]
|
||||
}
|
||||
|
||||
// Right returns the right of the bound.
|
||||
func (b Bound) Right() float64 {
|
||||
return b.Max[0]
|
||||
}
|
||||
|
||||
// Left returns the left of the bound.
|
||||
func (b Bound) Left() float64 {
|
||||
return b.Min[0]
|
||||
}
|
||||
|
||||
// LeftTop returns the upper left point of the bound.
|
||||
func (b Bound) LeftTop() Point {
|
||||
return Point{b.Left(), b.Top()}
|
||||
}
|
||||
|
||||
// RightBottom return the lower right point of the bound.
|
||||
func (b Bound) RightBottom() Point {
|
||||
return Point{b.Right(), b.Bottom()}
|
||||
}
|
||||
|
||||
// IsEmpty returns true if it contains zero area or if
|
||||
// it's in some malformed negative state where the left point is larger than the right.
|
||||
// This can be caused by padding too much negative.
|
||||
func (b Bound) IsEmpty() bool {
|
||||
return b.Min[0] > b.Max[0] || b.Min[1] > b.Max[1]
|
||||
}
|
||||
|
||||
// IsZero return true if the bound just includes just null island.
|
||||
func (b Bound) IsZero() bool {
|
||||
return b.Max == Point{} && b.Min == Point{}
|
||||
}
|
||||
|
||||
// Bound returns the the same bound.
|
||||
func (b Bound) Bound() Bound {
|
||||
return b
|
||||
}
|
||||
|
||||
// Equal returns if two bounds are equal.
|
||||
func (b Bound) Equal(c Bound) bool {
|
||||
return b.Min == c.Min && b.Max == c.Max
|
||||
}
|
||||
56
vendor/github.com/paulmach/orb/clone.go
generated
vendored
Normal file
56
vendor/github.com/paulmach/orb/clone.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
package orb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Clone will make a deep copy of the geometry.
|
||||
func Clone(g Geometry) Geometry {
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch g := g.(type) {
|
||||
case Point:
|
||||
return g
|
||||
case MultiPoint:
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
return g.Clone()
|
||||
case LineString:
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
return g.Clone()
|
||||
case MultiLineString:
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
return g.Clone()
|
||||
case Ring:
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
return g.Clone()
|
||||
case Polygon:
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
return g.Clone()
|
||||
case MultiPolygon:
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
return g.Clone()
|
||||
case Collection:
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
return g.Clone()
|
||||
case Bound:
|
||||
return g
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("geometry type not supported: %T", g))
|
||||
}
|
||||
10
vendor/github.com/paulmach/orb/codecov.yml
generated
vendored
Normal file
10
vendor/github.com/paulmach/orb/codecov.yml
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
coverage:
|
||||
status:
|
||||
project: off
|
||||
patch: off
|
||||
|
||||
precision: 2
|
||||
round: down
|
||||
range: "70...90"
|
||||
|
||||
comment: false
|
||||
44
vendor/github.com/paulmach/orb/define.go
generated
vendored
Normal file
44
vendor/github.com/paulmach/orb/define.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package orb
|
||||
|
||||
// EarthRadius is the radius of the earth in meters. It is used in geo distance calculations.
|
||||
// To keep things consistent, this value matches WGS84 Web Mercator (EPSG:3857).
|
||||
const EarthRadius = 6378137.0 // meters
|
||||
|
||||
// DefaultRoundingFactor is the default rounding factor used by the Round func.
|
||||
var DefaultRoundingFactor = 1e6 // 6 decimal places
|
||||
|
||||
// Orientation defines the order of the points in a polygon
|
||||
// or closed ring.
|
||||
type Orientation int8
|
||||
|
||||
// Constants to define orientation.
|
||||
// They follow the right hand rule for orientation.
|
||||
const (
|
||||
// CCW stands for Counter Clock Wise
|
||||
CCW Orientation = 1
|
||||
|
||||
// CW stands for Clock Wise
|
||||
CW Orientation = -1
|
||||
)
|
||||
|
||||
// A DistanceFunc is a function that computes the distance between two points.
|
||||
type DistanceFunc func(Point, Point) float64
|
||||
|
||||
// A Projection a function that moves a point from one space to another.
|
||||
type Projection func(Point) Point
|
||||
|
||||
// Pointer is something that can be represented by a point.
|
||||
type Pointer interface {
|
||||
Point() Point
|
||||
}
|
||||
|
||||
// A Simplifier is something that can simplify geometry.
|
||||
type Simplifier interface {
|
||||
Simplify(g Geometry) Geometry
|
||||
LineString(ls LineString) LineString
|
||||
MultiLineString(mls MultiLineString) MultiLineString
|
||||
Ring(r Ring) Ring
|
||||
Polygon(p Polygon) Polygon
|
||||
MultiPolygon(mp MultiPolygon) MultiPolygon
|
||||
Collection(c Collection) Collection
|
||||
}
|
||||
51
vendor/github.com/paulmach/orb/equal.go
generated
vendored
Normal file
51
vendor/github.com/paulmach/orb/equal.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package orb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Equal returns if the two geometrires are equal.
|
||||
func Equal(g1, g2 Geometry) bool {
|
||||
if g1 == nil || g2 == nil {
|
||||
return g1 == g2
|
||||
}
|
||||
|
||||
if g1.GeoJSONType() != g2.GeoJSONType() {
|
||||
return false
|
||||
}
|
||||
|
||||
switch g1 := g1.(type) {
|
||||
case Point:
|
||||
return g1.Equal(g2.(Point))
|
||||
case MultiPoint:
|
||||
return g1.Equal(g2.(MultiPoint))
|
||||
case LineString:
|
||||
return g1.Equal(g2.(LineString))
|
||||
case MultiLineString:
|
||||
return g1.Equal(g2.(MultiLineString))
|
||||
case Ring:
|
||||
g2, ok := g2.(Ring)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return g1.Equal(g2)
|
||||
case Polygon:
|
||||
g2, ok := g2.(Polygon)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return g1.Equal(g2)
|
||||
case MultiPolygon:
|
||||
return g1.Equal(g2.(MultiPolygon))
|
||||
case Collection:
|
||||
return g1.Equal(g2.(Collection))
|
||||
case Bound:
|
||||
g2, ok := g2.(Bound)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return g1.Equal(g2)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("geometry type not supported: %T", g1))
|
||||
}
|
||||
60
vendor/github.com/paulmach/orb/geo/README.md
generated
vendored
Normal file
60
vendor/github.com/paulmach/orb/geo/README.md
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
# orb/geo [](https://pkg.go.dev/github.com/paulmach/orb/geo)
|
||||
|
||||
The geometries defined in the `orb` package are generic 2d geometries.
|
||||
Depending on what projection they're in, e.g. lon/lat or flat on the plane,
|
||||
area and distance calculations are different. This package implements methods
|
||||
that assume the lon/lat or WGS84 projection.
|
||||
|
||||
## Examples
|
||||
|
||||
Area of the [San Francisco Main Library](https://www.openstreetmap.org/way/24446086):
|
||||
|
||||
```go
|
||||
poly := orb.Polygon{
|
||||
{
|
||||
{ -122.4163816, 37.7792782 },
|
||||
{ -122.4162786, 37.7787626 },
|
||||
{ -122.4151027, 37.7789118 },
|
||||
{ -122.4152143, 37.7794274 },
|
||||
{ -122.4163816, 37.7792782 },
|
||||
},
|
||||
}
|
||||
|
||||
a := geo.Area(poly)
|
||||
|
||||
fmt.Printf("%f m^2", a)
|
||||
// Output:
|
||||
// 6073.368008 m^2
|
||||
```
|
||||
|
||||
Distance between two points:
|
||||
|
||||
```go
|
||||
oakland := orb.Point{-122.270833, 37.804444}
|
||||
sf := orb.Point{-122.416667, 37.783333}
|
||||
|
||||
d := geo.Distance(oakland, sf)
|
||||
|
||||
fmt.Printf("%0.3f meters", d)
|
||||
// Output:
|
||||
// 13042.047 meters
|
||||
```
|
||||
|
||||
Circumference of the [San Francisco Main Library](https://www.openstreetmap.org/way/24446086):
|
||||
|
||||
```go
|
||||
poly := orb.Polygon{
|
||||
{
|
||||
{ -122.4163816, 37.7792782 },
|
||||
{ -122.4162786, 37.7787626 },
|
||||
{ -122.4151027, 37.7789118 },
|
||||
{ -122.4152143, 37.7794274 },
|
||||
{ -122.4163816, 37.7792782 },
|
||||
},
|
||||
}
|
||||
l := geo.Length(poly)
|
||||
|
||||
fmt.Printf("%0.0f meters", l)
|
||||
// Output:
|
||||
// 325 meters
|
||||
```
|
||||
112
vendor/github.com/paulmach/orb/geo/area.go
generated
vendored
Normal file
112
vendor/github.com/paulmach/orb/geo/area.go
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
// Package geo computes properties on geometries assuming they are lon/lat data.
|
||||
package geo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/paulmach/orb"
|
||||
)
|
||||
|
||||
// Area returns the area of the geometry on the earth.
|
||||
func Area(g orb.Geometry) float64 {
|
||||
if g == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
switch g := g.(type) {
|
||||
case orb.Point, orb.MultiPoint, orb.LineString, orb.MultiLineString:
|
||||
return 0
|
||||
case orb.Ring:
|
||||
return math.Abs(ringArea(g))
|
||||
case orb.Polygon:
|
||||
return polygonArea(g)
|
||||
case orb.MultiPolygon:
|
||||
return multiPolygonArea(g)
|
||||
case orb.Collection:
|
||||
return collectionArea(g)
|
||||
case orb.Bound:
|
||||
return Area(g.ToRing())
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("geometry type not supported: %T", g))
|
||||
}
|
||||
|
||||
// SignedArea will return the signed area of the ring.
|
||||
// Will return negative if the ring is in the clockwise direction.
|
||||
// Will implicitly close the ring.
|
||||
func SignedArea(r orb.Ring) float64 {
|
||||
return ringArea(r)
|
||||
}
|
||||
|
||||
func ringArea(r orb.Ring) float64 {
|
||||
if len(r) < 3 {
|
||||
return 0
|
||||
}
|
||||
var lo, mi, hi int
|
||||
|
||||
l := len(r)
|
||||
if r[0] != r[len(r)-1] {
|
||||
// if not a closed ring, add an implicit calc for that last point.
|
||||
l++
|
||||
}
|
||||
|
||||
// To support implicit closing of ring, replace references to
|
||||
// the last point in r to the first 1.
|
||||
|
||||
area := 0.0
|
||||
for i := 0; i < l; i++ {
|
||||
if i == l-3 { // i = N-3
|
||||
lo = l - 3
|
||||
mi = l - 2
|
||||
hi = 0
|
||||
} else if i == l-2 { // i = N-2
|
||||
lo = l - 2
|
||||
mi = 0
|
||||
hi = 0
|
||||
} else if i == l-1 { // i = N-1
|
||||
lo = 0
|
||||
mi = 0
|
||||
hi = 1
|
||||
} else { // i = 0 to N-3
|
||||
lo = i
|
||||
mi = i + 1
|
||||
hi = i + 2
|
||||
}
|
||||
|
||||
area += (deg2rad(r[hi][0]) - deg2rad(r[lo][0])) * math.Sin(deg2rad(r[mi][1]))
|
||||
}
|
||||
|
||||
return -area * orb.EarthRadius * orb.EarthRadius / 2
|
||||
}
|
||||
|
||||
func polygonArea(p orb.Polygon) float64 {
|
||||
if len(p) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
sum := math.Abs(ringArea(p[0]))
|
||||
for i := 1; i < len(p); i++ {
|
||||
sum -= math.Abs(ringArea(p[i]))
|
||||
}
|
||||
|
||||
return sum
|
||||
}
|
||||
|
||||
func multiPolygonArea(mp orb.MultiPolygon) float64 {
|
||||
sum := 0.0
|
||||
for _, p := range mp {
|
||||
sum += polygonArea(p)
|
||||
}
|
||||
|
||||
return sum
|
||||
}
|
||||
|
||||
func collectionArea(c orb.Collection) float64 {
|
||||
area := 0.0
|
||||
for _, g := range c {
|
||||
area += Area(g)
|
||||
}
|
||||
|
||||
return area
|
||||
}
|
||||
97
vendor/github.com/paulmach/orb/geo/bound.go
generated
vendored
Normal file
97
vendor/github.com/paulmach/orb/geo/bound.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
package geo
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/paulmach/orb"
|
||||
)
|
||||
|
||||
// NewBoundAroundPoint creates a new bound given a center point,
|
||||
// and a distance from the center point in meters.
|
||||
func NewBoundAroundPoint(center orb.Point, distance float64) orb.Bound {
|
||||
radDist := distance / orb.EarthRadius
|
||||
radLat := deg2rad(center[1])
|
||||
radLon := deg2rad(center[0])
|
||||
minLat := radLat - radDist
|
||||
maxLat := radLat + radDist
|
||||
|
||||
var minLon, maxLon float64
|
||||
if minLat > minLatitude && maxLat < maxLatitude {
|
||||
deltaLon := math.Asin(math.Sin(radDist) / math.Cos(radLat))
|
||||
minLon = radLon - deltaLon
|
||||
if minLon < minLongitude {
|
||||
minLon += 2 * math.Pi
|
||||
}
|
||||
maxLon = radLon + deltaLon
|
||||
if maxLon > maxLongitude {
|
||||
maxLon -= 2 * math.Pi
|
||||
}
|
||||
} else {
|
||||
minLat = math.Max(minLat, minLatitude)
|
||||
maxLat = math.Min(maxLat, maxLatitude)
|
||||
minLon = minLongitude
|
||||
maxLon = maxLongitude
|
||||
}
|
||||
|
||||
return orb.Bound{
|
||||
Min: orb.Point{rad2deg(minLon), rad2deg(minLat)},
|
||||
Max: orb.Point{rad2deg(maxLon), rad2deg(maxLat)},
|
||||
}
|
||||
}
|
||||
|
||||
// BoundPad expands the bound in all directions by the given amount of meters.
|
||||
func BoundPad(b orb.Bound, meters float64) orb.Bound {
|
||||
dy := meters / 111131.75
|
||||
dx := dy / math.Cos(deg2rad(b.Max[1]))
|
||||
dx = math.Max(dx, dy/math.Cos(deg2rad(b.Min[1])))
|
||||
|
||||
b.Min[0] -= dx
|
||||
b.Min[1] -= dy
|
||||
|
||||
b.Max[0] += dx
|
||||
b.Max[1] += dy
|
||||
|
||||
b.Min[0] = math.Max(b.Min[0], -180)
|
||||
b.Min[1] = math.Max(b.Min[1], -90)
|
||||
|
||||
b.Max[0] = math.Min(b.Max[0], 180)
|
||||
b.Max[1] = math.Min(b.Max[1], 90)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// BoundHeight returns the approximate height in meters.
|
||||
func BoundHeight(b orb.Bound) float64 {
|
||||
return 111131.75 * (b.Max[1] - b.Min[1])
|
||||
}
|
||||
|
||||
// BoundWidth returns the approximate width in meters
|
||||
// of the center of the bound.
|
||||
func BoundWidth(b orb.Bound) float64 {
|
||||
c := (b.Min[1] + b.Max[1]) / 2.0
|
||||
|
||||
s1 := orb.Point{b.Min[0], c}
|
||||
s2 := orb.Point{b.Max[0], c}
|
||||
|
||||
return Distance(s1, s2)
|
||||
}
|
||||
|
||||
//MinLatitude is the minimum possible latitude
|
||||
var minLatitude = deg2rad(-90)
|
||||
|
||||
//MaxLatitude is the maxiumum possible latitude
|
||||
var maxLatitude = deg2rad(90)
|
||||
|
||||
//MinLongitude is the minimum possible longitude
|
||||
var minLongitude = deg2rad(-180)
|
||||
|
||||
//MaxLongitude is the maxiumum possible longitude
|
||||
var maxLongitude = deg2rad(180)
|
||||
|
||||
func deg2rad(d float64) float64 {
|
||||
return d * math.Pi / 180.0
|
||||
}
|
||||
|
||||
func rad2deg(r float64) float64 {
|
||||
return 180.0 * r / math.Pi
|
||||
}
|
||||
119
vendor/github.com/paulmach/orb/geo/distance.go
generated
vendored
Normal file
119
vendor/github.com/paulmach/orb/geo/distance.go
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
package geo
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/paulmach/orb"
|
||||
)
|
||||
|
||||
// Distance returns the distance between two points on the earth.
|
||||
func Distance(p1, p2 orb.Point) float64 {
|
||||
dLat := deg2rad(p1[1] - p2[1])
|
||||
dLon := deg2rad(p1[0] - p2[0])
|
||||
|
||||
dLon = math.Abs(dLon)
|
||||
if dLon > math.Pi {
|
||||
dLon = 2*math.Pi - dLon
|
||||
}
|
||||
|
||||
// fast way using pythagorean theorem on an equirectangular projection
|
||||
x := dLon * math.Cos(deg2rad((p1[1]+p2[1])/2.0))
|
||||
return math.Sqrt(dLat*dLat+x*x) * orb.EarthRadius
|
||||
}
|
||||
|
||||
// DistanceHaversine computes the distance on the earth using the
|
||||
// more accurate haversine formula.
|
||||
func DistanceHaversine(p1, p2 orb.Point) float64 {
|
||||
dLat := deg2rad(p1[1] - p2[1])
|
||||
dLon := deg2rad(p1[0] - p2[0])
|
||||
|
||||
dLat2Sin := math.Sin(dLat / 2)
|
||||
dLon2Sin := math.Sin(dLon / 2)
|
||||
a := dLat2Sin*dLat2Sin + math.Cos(deg2rad(p2[1]))*math.Cos(deg2rad(p1[1]))*dLon2Sin*dLon2Sin
|
||||
|
||||
return 2.0 * orb.EarthRadius * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
|
||||
}
|
||||
|
||||
// Bearing computes the direction one must start traveling on earth
|
||||
// to be heading from, to the given points.
|
||||
func Bearing(from, to orb.Point) float64 {
|
||||
dLon := deg2rad(to[0] - from[0])
|
||||
|
||||
fromLatRad := deg2rad(from[1])
|
||||
toLatRad := deg2rad(to[1])
|
||||
|
||||
y := math.Sin(dLon) * math.Cos(toLatRad)
|
||||
x := math.Cos(fromLatRad)*math.Sin(toLatRad) - math.Sin(fromLatRad)*math.Cos(toLatRad)*math.Cos(dLon)
|
||||
|
||||
return rad2deg(math.Atan2(y, x))
|
||||
}
|
||||
|
||||
// Midpoint returns the half-way point along a great circle path between the two points.
|
||||
func Midpoint(p, p2 orb.Point) orb.Point {
|
||||
dLon := deg2rad(p2[0] - p[0])
|
||||
|
||||
aLatRad := deg2rad(p[1])
|
||||
bLatRad := deg2rad(p2[1])
|
||||
|
||||
x := math.Cos(bLatRad) * math.Cos(dLon)
|
||||
y := math.Cos(bLatRad) * math.Sin(dLon)
|
||||
|
||||
r := orb.Point{
|
||||
deg2rad(p[0]) + math.Atan2(y, math.Cos(aLatRad)+x),
|
||||
math.Atan2(math.Sin(aLatRad)+math.Sin(bLatRad), math.Sqrt((math.Cos(aLatRad)+x)*(math.Cos(aLatRad)+x)+y*y)),
|
||||
}
|
||||
|
||||
// convert back to degrees
|
||||
r[0] = rad2deg(r[0])
|
||||
r[1] = rad2deg(r[1])
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// PointAtBearingAndDistance returns the point at the given bearing and distance in meters from the point
|
||||
func PointAtBearingAndDistance(p orb.Point, bearing, distance float64) orb.Point {
|
||||
aLat := deg2rad(p[1])
|
||||
aLon := deg2rad(p[0])
|
||||
|
||||
bearingRadians := deg2rad(bearing)
|
||||
|
||||
distanceRatio := distance / orb.EarthRadius
|
||||
bLat := math.Asin(math.Sin(aLat)*math.Cos(distanceRatio) + math.Cos(aLat)*math.Sin(distanceRatio)*math.Cos(bearingRadians))
|
||||
bLon := aLon +
|
||||
math.Atan2(
|
||||
math.Sin(bearingRadians)*math.Sin(distanceRatio)*math.Cos(aLat),
|
||||
math.Cos(distanceRatio)-math.Sin(aLat)*math.Sin(bLat),
|
||||
)
|
||||
|
||||
return orb.Point{rad2deg(bLon), rad2deg(bLat)}
|
||||
}
|
||||
|
||||
func PointAtDistanceAlongLine(ls orb.LineString, distance float64) (orb.Point, float64) {
|
||||
if len(ls) == 0 {
|
||||
panic("empty LineString")
|
||||
}
|
||||
|
||||
if distance < 0 || len(ls) == 1 {
|
||||
return ls[0], 0.0
|
||||
}
|
||||
|
||||
var (
|
||||
travelled = 0.0
|
||||
from, to orb.Point
|
||||
)
|
||||
|
||||
for i := 1; i < len(ls); i++ {
|
||||
from, to = ls[i-1], ls[i]
|
||||
|
||||
actualSegmentDistance := DistanceHaversine(from, to)
|
||||
expectedSegmentDistance := distance - travelled
|
||||
|
||||
if expectedSegmentDistance < actualSegmentDistance {
|
||||
bearing := Bearing(from, to)
|
||||
return PointAtBearingAndDistance(from, bearing, expectedSegmentDistance), bearing
|
||||
}
|
||||
travelled += actualSegmentDistance
|
||||
}
|
||||
|
||||
return to, Bearing(from, to)
|
||||
}
|
||||
26
vendor/github.com/paulmach/orb/geo/length.go
generated
vendored
Normal file
26
vendor/github.com/paulmach/orb/geo/length.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package geo
|
||||
|
||||
import (
|
||||
"github.com/paulmach/orb"
|
||||
"github.com/paulmach/orb/internal/length"
|
||||
)
|
||||
|
||||
// Length returns the length of the boundary of the geometry
|
||||
// using the geo distance function.
|
||||
func Length(g orb.Geometry) float64 {
|
||||
return length.Length(g, Distance)
|
||||
}
|
||||
|
||||
// LengthHaversign returns the length of the boundary of the geometry
|
||||
// using the geo haversine formula
|
||||
//
|
||||
// Deprecated: misspelled, use correctly spelled `LengthHaversine` instead.
|
||||
func LengthHaversign(g orb.Geometry) float64 {
|
||||
return length.Length(g, DistanceHaversine)
|
||||
}
|
||||
|
||||
// LengthHaversine returns the length of the boundary of the geometry
|
||||
// using the geo haversine formula
|
||||
func LengthHaversine(g orb.Geometry) float64 {
|
||||
return length.Length(g, DistanceHaversine)
|
||||
}
|
||||
132
vendor/github.com/paulmach/orb/geojson/README.md
generated
vendored
Normal file
132
vendor/github.com/paulmach/orb/geojson/README.md
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
# orb/geojson [](https://pkg.go.dev/github.com/paulmach/orb/geojson)
|
||||
|
||||
This package **encodes and decodes** [GeoJSON](http://geojson.org/) into Go structs
|
||||
using the geometries in the [orb](https://github.com/paulmach/orb) package.
|
||||
|
||||
Supports both the [json.Marshaler](https://pkg.go.dev/encoding/json#Marshaler) and
|
||||
[json.Unmarshaler](https://pkg.go.dev/encoding/json#Unmarshaler) interfaces.
|
||||
The package also provides helper functions such as `UnmarshalFeatureCollection` and `UnmarshalFeature`.
|
||||
|
||||
The types also support BSON via the [bson.Marshaler](https://pkg.go.dev/go.mongodb.org/mongo-driver/bson#Marshaler) and
|
||||
[bson.Unmarshaler](https://pkg.go.dev/go.mongodb.org/mongo-driver/bson#Unmarshaler) interfaces.
|
||||
These types can be used directly when working with MongoDB.
|
||||
|
||||
## Unmarshalling (JSON -> Go)
|
||||
|
||||
```go
|
||||
rawJSON := []byte(`
|
||||
{ "type": "FeatureCollection",
|
||||
"features": [
|
||||
{ "type": "Feature",
|
||||
"geometry": {"type": "Point", "coordinates": [102.0, 0.5]},
|
||||
"properties": {"prop0": "value0"}
|
||||
}
|
||||
]
|
||||
}`)
|
||||
|
||||
fc, _ := geojson.UnmarshalFeatureCollection(rawJSON)
|
||||
|
||||
// or
|
||||
|
||||
fc := geojson.NewFeatureCollection()
|
||||
err := json.Unmarshal(rawJSON, &fc)
|
||||
|
||||
// Geometry will be unmarshalled into the correct geo.Geometry type.
|
||||
point := fc.Features[0].Geometry.(orb.Point)
|
||||
```
|
||||
|
||||
## Marshalling (Go -> JSON)
|
||||
|
||||
```go
|
||||
fc := geojson.NewFeatureCollection()
|
||||
fc.Append(geojson.NewFeature(orb.Point{1, 2}))
|
||||
|
||||
rawJSON, _ := fc.MarshalJSON()
|
||||
|
||||
// or
|
||||
blob, _ := json.Marshal(fc)
|
||||
```
|
||||
|
||||
## Foreign/extra members in a feature collection
|
||||
|
||||
```go
|
||||
rawJSON := []byte(`
|
||||
{ "type": "FeatureCollection",
|
||||
"generator": "myapp",
|
||||
"timestamp": "2020-06-15T01:02:03Z",
|
||||
"features": [
|
||||
{ "type": "Feature",
|
||||
"geometry": {"type": "Point", "coordinates": [102.0, 0.5]},
|
||||
"properties": {"prop0": "value0"}
|
||||
}
|
||||
]
|
||||
}`)
|
||||
|
||||
fc, _ := geojson.UnmarshalFeatureCollection(rawJSON)
|
||||
|
||||
fc.ExtraMembers["generator"] // == "myApp"
|
||||
fc.ExtraMembers["timestamp"] // == "2020-06-15T01:02:03Z"
|
||||
|
||||
// Marshalling will include values in `ExtraMembers` in the
|
||||
// base featureCollection object.
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
For performance critical applications, consider a
|
||||
third party replacement of "encoding/json" like [github.com/json-iterator/go](https://github.com/json-iterator/go)
|
||||
|
||||
This can be enabled with something like this:
|
||||
|
||||
```go
|
||||
import (
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/paulmach/orb"
|
||||
)
|
||||
|
||||
var c = jsoniter.Config{
|
||||
EscapeHTML: true,
|
||||
SortMapKeys: false,
|
||||
MarshalFloatWith6Digits: true,
|
||||
}.Froze()
|
||||
|
||||
CustomJSONMarshaler = c
|
||||
CustomJSONUnmarshaler = c
|
||||
```
|
||||
|
||||
The above change can have dramatic performance implications, see the benchmarks below
|
||||
on a 100k feature collection file:
|
||||
|
||||
```
|
||||
benchmark old ns/op new ns/op delta
|
||||
BenchmarkFeatureMarshalJSON-12 2694543 733480 -72.78%
|
||||
BenchmarkFeatureUnmarshalJSON-12 5383825 2738183 -49.14%
|
||||
BenchmarkGeometryMarshalJSON-12 210107 62789 -70.12%
|
||||
BenchmarkGeometryUnmarshalJSON-12 691472 144689 -79.08%
|
||||
|
||||
benchmark old allocs new allocs delta
|
||||
BenchmarkFeatureMarshalJSON-12 7818 2316 -70.38%
|
||||
BenchmarkFeatureUnmarshalJSON-12 23047 31946 +38.61%
|
||||
BenchmarkGeometryMarshalJSON-12 2 3 +50.00%
|
||||
BenchmarkGeometryUnmarshalJSON-12 2042 18 -99.12%
|
||||
|
||||
benchmark old bytes new bytes delta
|
||||
BenchmarkFeatureMarshalJSON-12 794088 490251 -38.26%
|
||||
BenchmarkFeatureUnmarshalJSON-12 766354 1068497 +39.43%
|
||||
BenchmarkGeometryMarshalJSON-12 24787 18650 -24.76%
|
||||
BenchmarkGeometryUnmarshalJSON-12 79784 51374 -35.61%
|
||||
```
|
||||
|
||||
## Feature Properties
|
||||
|
||||
GeoJSON features can have properties of any type. This can cause issues in a statically typed
|
||||
language such as Go. Included is a `Properties` type with some helper methods that will try to
|
||||
force convert a property. An optional default, will be used if the property is missing or the wrong
|
||||
type.
|
||||
|
||||
```go
|
||||
f.Properties.MustBool(key string, def ...bool) bool
|
||||
f.Properties.MustFloat64(key string, def ...float64) float64
|
||||
f.Properties.MustInt(key string, def ...int) int
|
||||
f.Properties.MustString(key string, def ...string) string
|
||||
```
|
||||
38
vendor/github.com/paulmach/orb/geojson/bbox.go
generated
vendored
Normal file
38
vendor/github.com/paulmach/orb/geojson/bbox.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package geojson
|
||||
|
||||
import "github.com/paulmach/orb"
|
||||
|
||||
// BBox is for the geojson bbox attribute which is an array with all axes
|
||||
// of the most southwesterly point followed by all axes of the more northeasterly point.
|
||||
type BBox []float64
|
||||
|
||||
// NewBBox creates a bbox from a a bound.
|
||||
func NewBBox(b orb.Bound) BBox {
|
||||
return []float64{
|
||||
b.Min[0], b.Min[1],
|
||||
b.Max[0], b.Max[1],
|
||||
}
|
||||
}
|
||||
|
||||
// Valid checks if the bbox is present and has at least 4 elements.
|
||||
func (bb BBox) Valid() bool {
|
||||
if bb == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(bb) >= 4 && len(bb)%2 == 0
|
||||
}
|
||||
|
||||
// Bound returns the orb.Bound for the BBox.
|
||||
func (bb BBox) Bound() orb.Bound {
|
||||
if !bb.Valid() {
|
||||
return orb.Bound{}
|
||||
}
|
||||
|
||||
mid := len(bb) / 2
|
||||
|
||||
return orb.Bound{
|
||||
Min: orb.Point{bb[0], bb[1]},
|
||||
Max: orb.Point{bb[mid], bb[mid+1]},
|
||||
}
|
||||
}
|
||||
138
vendor/github.com/paulmach/orb/geojson/feature.go
generated
vendored
Normal file
138
vendor/github.com/paulmach/orb/geojson/feature.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
package geojson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/paulmach/orb"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
// A Feature corresponds to GeoJSON feature object
|
||||
type Feature struct {
|
||||
ID interface{} `json:"id,omitempty"`
|
||||
Type string `json:"type"`
|
||||
BBox BBox `json:"bbox,omitempty"`
|
||||
Geometry orb.Geometry `json:"geometry"`
|
||||
Properties Properties `json:"properties"`
|
||||
}
|
||||
|
||||
// NewFeature creates and initializes a GeoJSON feature given the required attributes.
|
||||
func NewFeature(geometry orb.Geometry) *Feature {
|
||||
return &Feature{
|
||||
Type: "Feature",
|
||||
Geometry: geometry,
|
||||
Properties: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Point implements the orb.Pointer interface so that Features can be used
|
||||
// with quadtrees. The point returned is the center of the Bound of the geometry.
|
||||
// To represent the geometry with another point you must create a wrapper type.
|
||||
func (f *Feature) Point() orb.Point {
|
||||
return f.Geometry.Bound().Center()
|
||||
}
|
||||
|
||||
var _ orb.Pointer = &Feature{}
|
||||
|
||||
// MarshalJSON converts the feature object into the proper JSON.
|
||||
// It will handle the encoding of all the child geometries.
|
||||
// Alternately one can call json.Marshal(f) directly for the same result.
|
||||
func (f Feature) MarshalJSON() ([]byte, error) {
|
||||
return marshalJSON(newFeatureDoc(&f))
|
||||
}
|
||||
|
||||
// MarshalBSON converts the feature object into the proper JSON.
|
||||
// It will handle the encoding of all the child geometries.
|
||||
// Alternately one can call json.Marshal(f) directly for the same result.
|
||||
func (f Feature) MarshalBSON() ([]byte, error) {
|
||||
return bson.Marshal(newFeatureDoc(&f))
|
||||
}
|
||||
|
||||
func newFeatureDoc(f *Feature) *featureDoc {
|
||||
doc := &featureDoc{
|
||||
ID: f.ID,
|
||||
Type: "Feature",
|
||||
Properties: f.Properties,
|
||||
BBox: f.BBox,
|
||||
Geometry: NewGeometry(f.Geometry),
|
||||
}
|
||||
|
||||
if len(doc.Properties) == 0 {
|
||||
doc.Properties = nil
|
||||
}
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
// UnmarshalFeature decodes the data into a GeoJSON feature.
|
||||
// Alternately one can call json.Unmarshal(f) directly for the same result.
|
||||
func UnmarshalFeature(data []byte) (*Feature, error) {
|
||||
f := &Feature{}
|
||||
err := f.UnmarshalJSON(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON handles the correct unmarshalling of the data
|
||||
// into the orb.Geometry types.
|
||||
func (f *Feature) UnmarshalJSON(data []byte) error {
|
||||
if bytes.Equal(data, []byte(`null`)) {
|
||||
*f = Feature{}
|
||||
return nil
|
||||
}
|
||||
|
||||
doc := &featureDoc{}
|
||||
err := unmarshalJSON(data, &doc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return featureUnmarshalFinish(doc, f)
|
||||
}
|
||||
|
||||
// UnmarshalBSON will unmarshal a BSON document created with bson.Marshal.
|
||||
func (f *Feature) UnmarshalBSON(data []byte) error {
|
||||
doc := &featureDoc{}
|
||||
err := bson.Unmarshal(data, &doc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return featureUnmarshalFinish(doc, f)
|
||||
}
|
||||
|
||||
func featureUnmarshalFinish(doc *featureDoc, f *Feature) error {
|
||||
if doc.Type != "Feature" {
|
||||
return fmt.Errorf("geojson: not a feature: type=%s", doc.Type)
|
||||
}
|
||||
|
||||
var g orb.Geometry
|
||||
if doc.Geometry != nil {
|
||||
if doc.Geometry.Coordinates == nil && doc.Geometry.Geometries == nil {
|
||||
return ErrInvalidGeometry
|
||||
}
|
||||
g = doc.Geometry.Geometry()
|
||||
}
|
||||
|
||||
*f = Feature{
|
||||
ID: doc.ID,
|
||||
Type: doc.Type,
|
||||
Properties: doc.Properties,
|
||||
BBox: doc.BBox,
|
||||
Geometry: g,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type featureDoc struct {
|
||||
ID interface{} `json:"id,omitempty" bson:"id"`
|
||||
Type string `json:"type" bson:"type"`
|
||||
BBox BBox `json:"bbox,omitempty" bson:"bbox,omitempty"`
|
||||
Geometry *Geometry `json:"geometry" bson:"geometry"`
|
||||
Properties Properties `json:"properties" bson:"properties"`
|
||||
}
|
||||
197
vendor/github.com/paulmach/orb/geojson/feature_collection.go
generated
vendored
Normal file
197
vendor/github.com/paulmach/orb/geojson/feature_collection.go
generated
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
Package geojson is a library for encoding and decoding GeoJSON into Go structs
|
||||
using the geometries in the orb package. Supports both the json.Marshaler and
|
||||
json.Unmarshaler interfaces as well as helper functions such as
|
||||
`UnmarshalFeatureCollection` and `UnmarshalFeature`.
|
||||
*/
|
||||
package geojson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
const featureCollection = "FeatureCollection"
|
||||
|
||||
// A FeatureCollection correlates to a GeoJSON feature collection.
|
||||
type FeatureCollection struct {
|
||||
Type string `json:"type"`
|
||||
BBox BBox `json:"bbox,omitempty"`
|
||||
Features []*Feature `json:"features"`
|
||||
|
||||
// ExtraMembers can be used to encoded/decode extra key/members in
|
||||
// the base of the feature collection. Note that keys of "type", "bbox"
|
||||
// and "features" will not work as those are reserved by the GeoJSON spec.
|
||||
ExtraMembers Properties `json:"-"`
|
||||
}
|
||||
|
||||
// NewFeatureCollection creates and initializes a new feature collection.
|
||||
func NewFeatureCollection() *FeatureCollection {
|
||||
return &FeatureCollection{
|
||||
Type: featureCollection,
|
||||
Features: []*Feature{},
|
||||
}
|
||||
}
|
||||
|
||||
// Append appends a feature to the collection.
|
||||
func (fc *FeatureCollection) Append(feature *Feature) *FeatureCollection {
|
||||
fc.Features = append(fc.Features, feature)
|
||||
return fc
|
||||
}
|
||||
|
||||
// MarshalJSON converts the feature collection object into the proper JSON.
|
||||
// It will handle the encoding of all the child features and geometries.
|
||||
// Alternately one can call json.Marshal(fc) directly for the same result.
|
||||
// Items in the ExtraMembers map will be included in the base of the
|
||||
// feature collection object.
|
||||
func (fc FeatureCollection) MarshalJSON() ([]byte, error) {
|
||||
m := newFeatureCollectionDoc(fc)
|
||||
return marshalJSON(m)
|
||||
}
|
||||
|
||||
// MarshalBSON converts the feature collection object into a BSON document
|
||||
// represented by bytes. It will handle the encoding of all the child features
|
||||
// and geometries.
|
||||
// Items in the ExtraMembers map will be included in the base of the
|
||||
// feature collection object.
|
||||
func (fc FeatureCollection) MarshalBSON() ([]byte, error) {
|
||||
m := newFeatureCollectionDoc(fc)
|
||||
return bson.Marshal(m)
|
||||
}
|
||||
|
||||
func newFeatureCollectionDoc(fc FeatureCollection) map[string]interface{} {
|
||||
var tmp map[string]interface{}
|
||||
if fc.ExtraMembers != nil {
|
||||
tmp = fc.ExtraMembers.Clone()
|
||||
} else {
|
||||
tmp = make(map[string]interface{}, 3)
|
||||
}
|
||||
|
||||
tmp["type"] = featureCollection
|
||||
delete(tmp, "bbox")
|
||||
if fc.BBox != nil {
|
||||
tmp["bbox"] = fc.BBox
|
||||
}
|
||||
if fc.Features == nil {
|
||||
tmp["features"] = []*Feature{}
|
||||
} else {
|
||||
tmp["features"] = fc.Features
|
||||
}
|
||||
|
||||
return tmp
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes the data into a GeoJSON feature collection.
|
||||
// Extra/foreign members will be put into the `ExtraMembers` attribute.
|
||||
func (fc *FeatureCollection) UnmarshalJSON(data []byte) error {
|
||||
if bytes.Equal(data, []byte(`null`)) {
|
||||
*fc = FeatureCollection{}
|
||||
return nil
|
||||
}
|
||||
|
||||
tmp := make(map[string]nocopyRawMessage, 4)
|
||||
|
||||
err := unmarshalJSON(data, &tmp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*fc = FeatureCollection{}
|
||||
for key, value := range tmp {
|
||||
switch key {
|
||||
case "type":
|
||||
err := unmarshalJSON(value, &fc.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "bbox":
|
||||
err := unmarshalJSON(value, &fc.BBox)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "features":
|
||||
err := unmarshalJSON(value, &fc.Features)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
if fc.ExtraMembers == nil {
|
||||
fc.ExtraMembers = Properties{}
|
||||
}
|
||||
|
||||
var val interface{}
|
||||
err := unmarshalJSON(value, &val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fc.ExtraMembers[key] = val
|
||||
}
|
||||
}
|
||||
|
||||
if fc.Type != featureCollection {
|
||||
return fmt.Errorf("geojson: not a feature collection: type=%s", fc.Type)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBSON will unmarshal a BSON document created with bson.Marshal.
|
||||
// Extra/foreign members will be put into the `ExtraMembers` attribute.
|
||||
func (fc *FeatureCollection) UnmarshalBSON(data []byte) error {
|
||||
tmp := make(map[string]bson.RawValue, 4)
|
||||
|
||||
err := bson.Unmarshal(data, &tmp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*fc = FeatureCollection{}
|
||||
for key, value := range tmp {
|
||||
switch key {
|
||||
case "type":
|
||||
fc.Type, _ = bson.RawValue(value).StringValueOK()
|
||||
case "bbox":
|
||||
err := value.Unmarshal(&fc.BBox)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "features":
|
||||
err := value.Unmarshal(&fc.Features)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
if fc.ExtraMembers == nil {
|
||||
fc.ExtraMembers = Properties{}
|
||||
}
|
||||
|
||||
var val interface{}
|
||||
err := value.Unmarshal(&val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fc.ExtraMembers[key] = val
|
||||
}
|
||||
}
|
||||
|
||||
if fc.Type != featureCollection {
|
||||
return fmt.Errorf("geojson: not a feature collection: type=%s", fc.Type)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalFeatureCollection decodes the data into a GeoJSON feature collection.
|
||||
// Alternately one can call json.Unmarshal(fc) directly for the same result.
|
||||
func UnmarshalFeatureCollection(data []byte) (*FeatureCollection, error) {
|
||||
fc := &FeatureCollection{}
|
||||
|
||||
err := fc.UnmarshalJSON(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fc, nil
|
||||
}
|
||||
586
vendor/github.com/paulmach/orb/geojson/geometry.go
generated
vendored
Normal file
586
vendor/github.com/paulmach/orb/geojson/geometry.go
generated
vendored
Normal file
@@ -0,0 +1,586 @@
|
||||
package geojson
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/paulmach/orb"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/bsontype"
|
||||
)
|
||||
|
||||
// ErrInvalidGeometry will be returned if a the json of the geometry is invalid.
|
||||
var ErrInvalidGeometry = errors.New("geojson: invalid geometry")
|
||||
|
||||
// A Geometry matches the structure of a GeoJSON Geometry.
|
||||
type Geometry struct {
|
||||
Type string `json:"type"`
|
||||
Coordinates orb.Geometry `json:"coordinates,omitempty"`
|
||||
Geometries []*Geometry `json:"geometries,omitempty"`
|
||||
}
|
||||
|
||||
// NewGeometry will create a Geometry object but will convert
|
||||
// the input into a GoeJSON geometry. For example, it will convert
|
||||
// Rings and Bounds into Polygons.
|
||||
func NewGeometry(g orb.Geometry) *Geometry {
|
||||
jg := &Geometry{}
|
||||
switch g := g.(type) {
|
||||
case orb.Ring:
|
||||
jg.Coordinates = orb.Polygon{g}
|
||||
case orb.Bound:
|
||||
jg.Coordinates = g.ToPolygon()
|
||||
case orb.Collection:
|
||||
for _, c := range g {
|
||||
jg.Geometries = append(jg.Geometries, NewGeometry(c))
|
||||
}
|
||||
jg.Type = g.GeoJSONType()
|
||||
default:
|
||||
jg.Coordinates = g
|
||||
}
|
||||
|
||||
if jg.Coordinates != nil {
|
||||
jg.Type = jg.Coordinates.GeoJSONType()
|
||||
}
|
||||
return jg
|
||||
}
|
||||
|
||||
// Geometry returns the orb.Geometry for the geojson Geometry.
|
||||
// This will convert the "Geometries" into a orb.Collection if applicable.
|
||||
func (g *Geometry) Geometry() orb.Geometry {
|
||||
if g.Coordinates != nil {
|
||||
return g.Coordinates
|
||||
}
|
||||
|
||||
c := make(orb.Collection, 0, len(g.Geometries))
|
||||
for _, geom := range g.Geometries {
|
||||
c = append(c, geom.Geometry())
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// MarshalJSON will marshal the geometry into the correct JSON structure.
|
||||
func (g *Geometry) MarshalJSON() ([]byte, error) {
|
||||
if g.Coordinates == nil && len(g.Geometries) == 0 {
|
||||
return []byte(`null`), nil
|
||||
}
|
||||
|
||||
ng := newGeometryMarshallDoc(g)
|
||||
return marshalJSON(ng)
|
||||
}
|
||||
|
||||
// MarshalBSON will convert the geometry into a BSON document with the structure
|
||||
// of a GeoJSON Geometry. This function is used when the geometry is the top level
|
||||
// document to be marshalled.
|
||||
func (g *Geometry) MarshalBSON() ([]byte, error) {
|
||||
ng := newGeometryMarshallDoc(g)
|
||||
return bson.Marshal(ng)
|
||||
}
|
||||
|
||||
// MarshalBSONValue will marshal the geometry into a BSON value
|
||||
// with the structure of a GeoJSON Geometry.
|
||||
func (g *Geometry) MarshalBSONValue() (bsontype.Type, []byte, error) {
|
||||
// implementing MarshalBSONValue allows us to marshal into a null value
|
||||
// needed to match behavior with the JSON marshalling.
|
||||
|
||||
if g.Coordinates == nil && len(g.Geometries) == 0 {
|
||||
return bsontype.Null, nil, nil
|
||||
}
|
||||
|
||||
ng := newGeometryMarshallDoc(g)
|
||||
return bson.MarshalValue(ng)
|
||||
}
|
||||
|
||||
func newGeometryMarshallDoc(g *Geometry) *geometryMarshallDoc {
|
||||
ng := &geometryMarshallDoc{}
|
||||
switch g := g.Coordinates.(type) {
|
||||
case orb.Ring:
|
||||
ng.Coordinates = orb.Polygon{g}
|
||||
case orb.Bound:
|
||||
ng.Coordinates = g.ToPolygon()
|
||||
case orb.Collection:
|
||||
ng.Geometries = make([]*Geometry, 0, len(g))
|
||||
for _, c := range g {
|
||||
ng.Geometries = append(ng.Geometries, NewGeometry(c))
|
||||
}
|
||||
ng.Type = g.GeoJSONType()
|
||||
default:
|
||||
ng.Coordinates = g
|
||||
}
|
||||
|
||||
if ng.Coordinates != nil {
|
||||
ng.Type = ng.Coordinates.GeoJSONType()
|
||||
}
|
||||
|
||||
if len(g.Geometries) > 0 {
|
||||
ng.Geometries = g.Geometries
|
||||
ng.Type = orb.Collection{}.GeoJSONType()
|
||||
}
|
||||
|
||||
return ng
|
||||
}
|
||||
|
||||
// UnmarshalGeometry decodes the JSON data into a GeoJSON feature.
|
||||
// Alternately one can call json.Unmarshal(g) directly for the same result.
|
||||
func UnmarshalGeometry(data []byte) (*Geometry, error) {
|
||||
g := &Geometry{}
|
||||
err := unmarshalJSON(data, g)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON will unmarshal the correct geometry from the JSON structure.
|
||||
func (g *Geometry) UnmarshalJSON(data []byte) error {
|
||||
jg := &jsonGeometry{}
|
||||
err := unmarshalJSON(data, jg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch jg.Type {
|
||||
case "Point":
|
||||
p := orb.Point{}
|
||||
err = unmarshalJSON(jg.Coordinates, &p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Coordinates = p
|
||||
case "MultiPoint":
|
||||
mp := orb.MultiPoint{}
|
||||
err = unmarshalJSON(jg.Coordinates, &mp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Coordinates = mp
|
||||
case "LineString":
|
||||
ls := orb.LineString{}
|
||||
err = unmarshalJSON(jg.Coordinates, &ls)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Coordinates = ls
|
||||
case "MultiLineString":
|
||||
mls := orb.MultiLineString{}
|
||||
err = unmarshalJSON(jg.Coordinates, &mls)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Coordinates = mls
|
||||
case "Polygon":
|
||||
p := orb.Polygon{}
|
||||
err = unmarshalJSON(jg.Coordinates, &p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Coordinates = p
|
||||
case "MultiPolygon":
|
||||
mp := orb.MultiPolygon{}
|
||||
err = unmarshalJSON(jg.Coordinates, &mp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Coordinates = mp
|
||||
case "GeometryCollection":
|
||||
g.Geometries = jg.Geometries
|
||||
default:
|
||||
return ErrInvalidGeometry
|
||||
}
|
||||
|
||||
g.Type = g.Geometry().GeoJSONType()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBSON will unmarshal a BSON document created with bson.Marshal.
|
||||
func (g *Geometry) UnmarshalBSON(data []byte) error {
|
||||
bg := &bsonGeometry{}
|
||||
err := bson.Unmarshal(data, bg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch bg.Type {
|
||||
case "Point":
|
||||
p := orb.Point{}
|
||||
err = bg.Coordinates.Unmarshal(&p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Coordinates = p
|
||||
case "MultiPoint":
|
||||
mp := orb.MultiPoint{}
|
||||
err = bg.Coordinates.Unmarshal(&mp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Coordinates = mp
|
||||
case "LineString":
|
||||
ls := orb.LineString{}
|
||||
|
||||
err = bg.Coordinates.Unmarshal(&ls)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Coordinates = ls
|
||||
case "MultiLineString":
|
||||
mls := orb.MultiLineString{}
|
||||
err = bg.Coordinates.Unmarshal(&mls)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Coordinates = mls
|
||||
case "Polygon":
|
||||
p := orb.Polygon{}
|
||||
err = bg.Coordinates.Unmarshal(&p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Coordinates = p
|
||||
case "MultiPolygon":
|
||||
mp := orb.MultiPolygon{}
|
||||
err = bg.Coordinates.Unmarshal(&mp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Coordinates = mp
|
||||
case "GeometryCollection":
|
||||
g.Geometries = bg.Geometries
|
||||
default:
|
||||
return ErrInvalidGeometry
|
||||
}
|
||||
|
||||
g.Type = g.Geometry().GeoJSONType()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// A Point is a helper type that will marshal to/from a GeoJSON Point geometry.
|
||||
type Point orb.Point
|
||||
|
||||
// Geometry will return the orb.Geometry version of the data.
|
||||
func (p Point) Geometry() orb.Geometry {
|
||||
return orb.Point(p)
|
||||
}
|
||||
|
||||
// MarshalJSON will convert the Point into a GeoJSON Point geometry.
|
||||
func (p Point) MarshalJSON() ([]byte, error) {
|
||||
return marshalJSON(&Geometry{Coordinates: orb.Point(p)})
|
||||
}
|
||||
|
||||
// MarshalBSON will convert the Point into a BSON value following the GeoJSON Point structure.
|
||||
func (p Point) MarshalBSON() ([]byte, error) {
|
||||
return bson.Marshal(&Geometry{Coordinates: orb.Point(p)})
|
||||
}
|
||||
|
||||
// UnmarshalJSON will unmarshal the GeoJSON Point geometry.
|
||||
func (p *Point) UnmarshalJSON(data []byte) error {
|
||||
g := &Geometry{}
|
||||
err := unmarshalJSON(data, &g)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
point, ok := g.Coordinates.(orb.Point)
|
||||
if !ok {
|
||||
return errors.New("geojson: not a Point type")
|
||||
}
|
||||
|
||||
*p = Point(point)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBSON will unmarshal GeoJSON Point geometry.
|
||||
func (p *Point) UnmarshalBSON(data []byte) error {
|
||||
g := &Geometry{}
|
||||
err := bson.Unmarshal(data, &g)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
point, ok := g.Coordinates.(orb.Point)
|
||||
if !ok {
|
||||
return errors.New("geojson: not a Point type")
|
||||
}
|
||||
|
||||
*p = Point(point)
|
||||
return nil
|
||||
}
|
||||
|
||||
// A MultiPoint is a helper type that will marshal to/from a GeoJSON MultiPoint geometry.
|
||||
type MultiPoint orb.MultiPoint
|
||||
|
||||
// Geometry will return the orb.Geometry version of the data.
|
||||
func (mp MultiPoint) Geometry() orb.Geometry {
|
||||
return orb.MultiPoint(mp)
|
||||
}
|
||||
|
||||
// MarshalJSON will convert the MultiPoint into a GeoJSON MultiPoint geometry.
|
||||
func (mp MultiPoint) MarshalJSON() ([]byte, error) {
|
||||
return marshalJSON(&Geometry{Coordinates: orb.MultiPoint(mp)})
|
||||
}
|
||||
|
||||
// MarshalBSON will convert the MultiPoint into a GeoJSON MultiPoint geometry BSON.
|
||||
func (mp MultiPoint) MarshalBSON() ([]byte, error) {
|
||||
return bson.Marshal(&Geometry{Coordinates: orb.MultiPoint(mp)})
|
||||
}
|
||||
|
||||
// UnmarshalJSON will unmarshal the GeoJSON MultiPoint geometry.
|
||||
func (mp *MultiPoint) UnmarshalJSON(data []byte) error {
|
||||
g := &Geometry{}
|
||||
err := unmarshalJSON(data, &g)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
multiPoint, ok := g.Coordinates.(orb.MultiPoint)
|
||||
if !ok {
|
||||
return errors.New("geojson: not a MultiPoint type")
|
||||
}
|
||||
|
||||
*mp = MultiPoint(multiPoint)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBSON will unmarshal the GeoJSON MultiPoint geometry.
|
||||
func (mp *MultiPoint) UnmarshalBSON(data []byte) error {
|
||||
g := &Geometry{}
|
||||
err := bson.Unmarshal(data, &g)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
multiPoint, ok := g.Coordinates.(orb.MultiPoint)
|
||||
if !ok {
|
||||
return errors.New("geojson: not a MultiPoint type")
|
||||
}
|
||||
|
||||
*mp = MultiPoint(multiPoint)
|
||||
return nil
|
||||
}
|
||||
|
||||
// A LineString is a helper type that will marshal to/from a GeoJSON LineString geometry.
|
||||
type LineString orb.LineString
|
||||
|
||||
// Geometry will return the orb.Geometry version of the data.
|
||||
func (ls LineString) Geometry() orb.Geometry {
|
||||
return orb.LineString(ls)
|
||||
}
|
||||
|
||||
// MarshalJSON will convert the LineString into a GeoJSON LineString geometry.
|
||||
func (ls LineString) MarshalJSON() ([]byte, error) {
|
||||
return marshalJSON(&Geometry{Coordinates: orb.LineString(ls)})
|
||||
}
|
||||
|
||||
// MarshalBSON will convert the LineString into a GeoJSON LineString geometry.
|
||||
func (ls LineString) MarshalBSON() ([]byte, error) {
|
||||
return bson.Marshal(&Geometry{Coordinates: orb.LineString(ls)})
|
||||
}
|
||||
|
||||
// UnmarshalJSON will unmarshal the GeoJSON MultiPoint geometry.
|
||||
func (ls *LineString) UnmarshalJSON(data []byte) error {
|
||||
g := &Geometry{}
|
||||
err := unmarshalJSON(data, &g)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lineString, ok := g.Coordinates.(orb.LineString)
|
||||
if !ok {
|
||||
return errors.New("geojson: not a LineString type")
|
||||
}
|
||||
|
||||
*ls = LineString(lineString)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBSON will unmarshal the GeoJSON MultiPoint geometry.
|
||||
func (ls *LineString) UnmarshalBSON(data []byte) error {
|
||||
g := &Geometry{}
|
||||
err := bson.Unmarshal(data, &g)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lineString, ok := g.Coordinates.(orb.LineString)
|
||||
if !ok {
|
||||
return errors.New("geojson: not a LineString type")
|
||||
}
|
||||
|
||||
*ls = LineString(lineString)
|
||||
return nil
|
||||
}
|
||||
|
||||
// A MultiLineString is a helper type that will marshal to/from a GeoJSON MultiLineString geometry.
|
||||
type MultiLineString orb.MultiLineString
|
||||
|
||||
// Geometry will return the orb.Geometry version of the data.
|
||||
func (mls MultiLineString) Geometry() orb.Geometry {
|
||||
return orb.MultiLineString(mls)
|
||||
}
|
||||
|
||||
// MarshalJSON will convert the MultiLineString into a GeoJSON MultiLineString geometry.
|
||||
func (mls MultiLineString) MarshalJSON() ([]byte, error) {
|
||||
return marshalJSON(&Geometry{Coordinates: orb.MultiLineString(mls)})
|
||||
}
|
||||
|
||||
// MarshalBSON will convert the MultiLineString into a GeoJSON MultiLineString geometry.
|
||||
func (mls MultiLineString) MarshalBSON() ([]byte, error) {
|
||||
return bson.Marshal(&Geometry{Coordinates: orb.MultiLineString(mls)})
|
||||
}
|
||||
|
||||
// UnmarshalJSON will unmarshal the GeoJSON MultiPoint geometry.
|
||||
func (mls *MultiLineString) UnmarshalJSON(data []byte) error {
|
||||
g := &Geometry{}
|
||||
err := unmarshalJSON(data, &g)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
multilineString, ok := g.Coordinates.(orb.MultiLineString)
|
||||
if !ok {
|
||||
return errors.New("geojson: not a MultiLineString type")
|
||||
}
|
||||
|
||||
*mls = MultiLineString(multilineString)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBSON will unmarshal the GeoJSON MultiPoint geometry.
|
||||
func (mls *MultiLineString) UnmarshalBSON(data []byte) error {
|
||||
g := &Geometry{}
|
||||
err := bson.Unmarshal(data, &g)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
multilineString, ok := g.Coordinates.(orb.MultiLineString)
|
||||
if !ok {
|
||||
return errors.New("geojson: not a MultiLineString type")
|
||||
}
|
||||
|
||||
*mls = MultiLineString(multilineString)
|
||||
return nil
|
||||
}
|
||||
|
||||
// A Polygon is a helper type that will marshal to/from a GeoJSON Polygon geometry.
|
||||
type Polygon orb.Polygon
|
||||
|
||||
// Geometry will return the orb.Geometry version of the data.
|
||||
func (p Polygon) Geometry() orb.Geometry {
|
||||
return orb.Polygon(p)
|
||||
}
|
||||
|
||||
// MarshalJSON will convert the Polygon into a GeoJSON Polygon geometry.
|
||||
func (p Polygon) MarshalJSON() ([]byte, error) {
|
||||
return marshalJSON(&Geometry{Coordinates: orb.Polygon(p)})
|
||||
}
|
||||
|
||||
// MarshalBSON will convert the Polygon into a GeoJSON Polygon geometry.
|
||||
func (p Polygon) MarshalBSON() ([]byte, error) {
|
||||
return bson.Marshal(&Geometry{Coordinates: orb.Polygon(p)})
|
||||
}
|
||||
|
||||
// UnmarshalJSON will unmarshal the GeoJSON Polygon geometry.
|
||||
func (p *Polygon) UnmarshalJSON(data []byte) error {
|
||||
g := &Geometry{}
|
||||
err := unmarshalJSON(data, &g)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
polygon, ok := g.Coordinates.(orb.Polygon)
|
||||
if !ok {
|
||||
return errors.New("geojson: not a Polygon type")
|
||||
}
|
||||
|
||||
*p = Polygon(polygon)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBSON will unmarshal the GeoJSON Polygon geometry.
|
||||
func (p *Polygon) UnmarshalBSON(data []byte) error {
|
||||
g := &Geometry{}
|
||||
err := bson.Unmarshal(data, &g)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
polygon, ok := g.Coordinates.(orb.Polygon)
|
||||
if !ok {
|
||||
return errors.New("geojson: not a Polygon type")
|
||||
}
|
||||
|
||||
*p = Polygon(polygon)
|
||||
return nil
|
||||
}
|
||||
|
||||
// A MultiPolygon is a helper type that will marshal to/from a GeoJSON MultiPolygon geometry.
|
||||
type MultiPolygon orb.MultiPolygon
|
||||
|
||||
// Geometry will return the orb.Geometry version of the data.
|
||||
func (mp MultiPolygon) Geometry() orb.Geometry {
|
||||
return orb.MultiPolygon(mp)
|
||||
}
|
||||
|
||||
// MarshalJSON will convert the MultiPolygon into a GeoJSON MultiPolygon geometry.
|
||||
func (mp MultiPolygon) MarshalJSON() ([]byte, error) {
|
||||
return marshalJSON(&Geometry{Coordinates: orb.MultiPolygon(mp)})
|
||||
}
|
||||
|
||||
// MarshalBSON will convert the MultiPolygon into a GeoJSON MultiPolygon geometry.
|
||||
func (mp MultiPolygon) MarshalBSON() ([]byte, error) {
|
||||
return bson.Marshal(&Geometry{Coordinates: orb.MultiPolygon(mp)})
|
||||
}
|
||||
|
||||
// UnmarshalJSON will unmarshal the GeoJSON MultiPolygon geometry.
|
||||
func (mp *MultiPolygon) UnmarshalJSON(data []byte) error {
|
||||
g := &Geometry{}
|
||||
err := unmarshalJSON(data, &g)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
multiPolygon, ok := g.Coordinates.(orb.MultiPolygon)
|
||||
if !ok {
|
||||
return errors.New("geojson: not a MultiPolygon type")
|
||||
}
|
||||
|
||||
*mp = MultiPolygon(multiPolygon)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBSON will unmarshal the GeoJSON MultiPolygon geometry.
|
||||
func (mp *MultiPolygon) UnmarshalBSON(data []byte) error {
|
||||
g := &Geometry{}
|
||||
err := bson.Unmarshal(data, &g)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
multiPolygon, ok := g.Coordinates.(orb.MultiPolygon)
|
||||
if !ok {
|
||||
return errors.New("geojson: not a MultiPolygon type")
|
||||
}
|
||||
|
||||
*mp = MultiPolygon(multiPolygon)
|
||||
return nil
|
||||
}
|
||||
|
||||
type bsonGeometry struct {
|
||||
Type string `json:"type" bson:"type"`
|
||||
Coordinates bson.RawValue `json:"coordinates" bson:"coordinates"`
|
||||
Geometries []*Geometry `json:"geometries,omitempty" bson:"geometries"`
|
||||
}
|
||||
|
||||
type jsonGeometry struct {
|
||||
Type string `json:"type"`
|
||||
Coordinates nocopyRawMessage `json:"coordinates"`
|
||||
Geometries []*Geometry `json:"geometries,omitempty"`
|
||||
}
|
||||
|
||||
type geometryMarshallDoc struct {
|
||||
Type string `json:"type" bson:"type"`
|
||||
Coordinates orb.Geometry `json:"coordinates,omitempty" bson:"coordinates,omitempty"`
|
||||
Geometries []*Geometry `json:"geometries,omitempty" bson:"geometries,omitempty"`
|
||||
}
|
||||
74
vendor/github.com/paulmach/orb/geojson/json.go
generated
vendored
Normal file
74
vendor/github.com/paulmach/orb/geojson/json.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package geojson
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// CustomJSONMarshaler can be set to have the code use a different
|
||||
// json marshaler than the default in the standard library.
|
||||
// One use case in enabling `github.com/json-iterator/go`
|
||||
// with something like this:
|
||||
//
|
||||
// import (
|
||||
// jsoniter "github.com/json-iterator/go"
|
||||
// "github.com/paulmach/orb"
|
||||
// )
|
||||
//
|
||||
// var c = jsoniter.Config{
|
||||
// EscapeHTML: true,
|
||||
// SortMapKeys: false,
|
||||
// MarshalFloatWith6Digits: true,
|
||||
// }.Froze()
|
||||
//
|
||||
// orb.CustomJSONMarshaler = c
|
||||
// orb.CustomJSONUnmarshaler = c
|
||||
//
|
||||
// Note that any errors encountered during marshaling will be different.
|
||||
var CustomJSONMarshaler interface {
|
||||
Marshal(v interface{}) ([]byte, error)
|
||||
} = nil
|
||||
|
||||
// CustomJSONUnmarshaler can be set to have the code use a different
|
||||
// json unmarshaler than the default in the standard library.
|
||||
// One use case in enabling `github.com/json-iterator/go`
|
||||
// with something like this:
|
||||
//
|
||||
// import (
|
||||
// jsoniter "github.com/json-iterator/go"
|
||||
// "github.com/paulmach/orb"
|
||||
// )
|
||||
//
|
||||
// var c = jsoniter.Config{
|
||||
// EscapeHTML: true,
|
||||
// SortMapKeys: false,
|
||||
// MarshalFloatWith6Digits: true,
|
||||
// }.Froze()
|
||||
//
|
||||
// orb.CustomJSONMarshaler = c
|
||||
// orb.CustomJSONUnmarshaler = c
|
||||
//
|
||||
// Note that any errors encountered during unmarshaling will be different.
|
||||
var CustomJSONUnmarshaler interface {
|
||||
Unmarshal(data []byte, v interface{}) error
|
||||
} = nil
|
||||
|
||||
func marshalJSON(v interface{}) ([]byte, error) {
|
||||
if CustomJSONMarshaler == nil {
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
return CustomJSONMarshaler.Marshal(v)
|
||||
}
|
||||
|
||||
func unmarshalJSON(data []byte, v interface{}) error {
|
||||
if CustomJSONUnmarshaler == nil {
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
return CustomJSONUnmarshaler.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
type nocopyRawMessage []byte
|
||||
|
||||
func (m *nocopyRawMessage) UnmarshalJSON(data []byte) error {
|
||||
*m = data
|
||||
return nil
|
||||
}
|
||||
112
vendor/github.com/paulmach/orb/geojson/properties.go
generated
vendored
Normal file
112
vendor/github.com/paulmach/orb/geojson/properties.go
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
package geojson
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Properties defines the feature properties with some helper methods.
|
||||
type Properties map[string]interface{}
|
||||
|
||||
// MustBool guarantees the return of a `bool` (with optional default).
|
||||
// This function useful when you explicitly want a `bool` in a single
|
||||
// value return context, for example:
|
||||
// myFunc(f.Properties.MustBool("param1"), f.Properties.MustBool("optional_param", true))
|
||||
// This function will panic if the value is present but not a bool.
|
||||
func (p Properties) MustBool(key string, def ...bool) bool {
|
||||
v := p[key]
|
||||
if b, ok := v.(bool); ok {
|
||||
return b
|
||||
}
|
||||
|
||||
if v != nil {
|
||||
panic(fmt.Sprintf("not a bool, but a %T: %v", v, v))
|
||||
}
|
||||
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
}
|
||||
|
||||
panic("property not found")
|
||||
}
|
||||
|
||||
// MustInt guarantees the return of an `int` (with optional default).
|
||||
// This function useful when you explicitly want a `int` in a single
|
||||
// value return context, for example:
|
||||
// myFunc(f.Properties.MustInt("param1"), f.Properties.MustInt("optional_param", 123))
|
||||
// This function will panic if the value is present but not a number.
|
||||
func (p Properties) MustInt(key string, def ...int) int {
|
||||
v := p[key]
|
||||
if i, ok := v.(int); ok {
|
||||
return i
|
||||
}
|
||||
|
||||
if f, ok := v.(float64); ok {
|
||||
return int(f)
|
||||
}
|
||||
|
||||
if v != nil {
|
||||
panic(fmt.Sprintf("not a number, but a %T: %v", v, v))
|
||||
}
|
||||
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
}
|
||||
|
||||
panic("property not found")
|
||||
}
|
||||
|
||||
// MustFloat64 guarantees the return of a `float64` (with optional default)
|
||||
// This function useful when you explicitly want a `float64` in a single
|
||||
// value return context, for example:
|
||||
// myFunc(f.Properties.MustFloat64("param1"), f.Properties.MustFloat64("optional_param", 10.1))
|
||||
// This function will panic if the value is present but not a number.
|
||||
func (p Properties) MustFloat64(key string, def ...float64) float64 {
|
||||
v := p[key]
|
||||
if f, ok := v.(float64); ok {
|
||||
return f
|
||||
}
|
||||
|
||||
if i, ok := v.(int); ok {
|
||||
return float64(i)
|
||||
}
|
||||
|
||||
if v != nil {
|
||||
panic(fmt.Sprintf("not a number, but a %T: %v", v, v))
|
||||
}
|
||||
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
}
|
||||
|
||||
panic("property not found")
|
||||
}
|
||||
|
||||
// MustString guarantees the return of a `string` (with optional default)
|
||||
// This function useful when you explicitly want a `string` in a single
|
||||
// value return context, for example:
|
||||
// myFunc(f.Properties.MustString("param1"), f.Properties.MustString("optional_param", "default"))
|
||||
// This function will panic if the value is present but not a string.
|
||||
func (p Properties) MustString(key string, def ...string) string {
|
||||
v := p[key]
|
||||
if s, ok := v.(string); ok {
|
||||
return s
|
||||
}
|
||||
|
||||
if v != nil {
|
||||
panic(fmt.Sprintf("not a string, but a %T: %v", v, v))
|
||||
}
|
||||
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
}
|
||||
|
||||
panic("property not found")
|
||||
}
|
||||
|
||||
// Clone returns a shallow copy of the properties.
|
||||
func (p Properties) Clone() Properties {
|
||||
n := make(Properties, len(p)+3)
|
||||
for k, v := range p {
|
||||
n[k] = v
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
11
vendor/github.com/paulmach/orb/geojson/types.go
generated
vendored
Normal file
11
vendor/github.com/paulmach/orb/geojson/types.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package geojson
|
||||
|
||||
// A list of the geojson types that are currently supported.
|
||||
const (
|
||||
TypePoint = "Point"
|
||||
TypeMultiPoint = "MultiPoint"
|
||||
TypeLineString = "LineString"
|
||||
TypeMultiLineString = "MultiLineString"
|
||||
TypePolygon = "Polygon"
|
||||
TypeMultiPolygon = "MultiPolygon"
|
||||
)
|
||||
146
vendor/github.com/paulmach/orb/geometry.go
generated
vendored
Normal file
146
vendor/github.com/paulmach/orb/geometry.go
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
package orb
|
||||
|
||||
// Geometry is an interface that represents the shared attributes
|
||||
// of a geometry.
|
||||
type Geometry interface {
|
||||
GeoJSONType() string
|
||||
Dimensions() int // e.g. 0d, 1d, 2d
|
||||
Bound() Bound
|
||||
|
||||
// requiring because sub package type switch over all possible types.
|
||||
private()
|
||||
}
|
||||
|
||||
// compile time checks
|
||||
var (
|
||||
_ Geometry = Point{}
|
||||
_ Geometry = MultiPoint{}
|
||||
_ Geometry = LineString{}
|
||||
_ Geometry = MultiLineString{}
|
||||
_ Geometry = Ring{}
|
||||
_ Geometry = Polygon{}
|
||||
_ Geometry = MultiPolygon{}
|
||||
_ Geometry = Bound{}
|
||||
|
||||
_ Geometry = Collection{}
|
||||
)
|
||||
|
||||
func (p Point) private() {}
|
||||
func (mp MultiPoint) private() {}
|
||||
func (ls LineString) private() {}
|
||||
func (mls MultiLineString) private() {}
|
||||
func (r Ring) private() {}
|
||||
func (p Polygon) private() {}
|
||||
func (mp MultiPolygon) private() {}
|
||||
func (b Bound) private() {}
|
||||
func (c Collection) private() {}
|
||||
|
||||
// AllGeometries lists all possible types and values that a geometry
|
||||
// interface can be. It should be used only for testing to verify
|
||||
// functions that accept a Geometry will work in all cases.
|
||||
var AllGeometries = []Geometry{
|
||||
nil,
|
||||
Point{},
|
||||
MultiPoint{},
|
||||
LineString{},
|
||||
MultiLineString{},
|
||||
Ring{},
|
||||
Polygon{},
|
||||
MultiPolygon{},
|
||||
Bound{},
|
||||
Collection{},
|
||||
|
||||
// nil values
|
||||
MultiPoint(nil),
|
||||
LineString(nil),
|
||||
MultiLineString(nil),
|
||||
Ring(nil),
|
||||
Polygon(nil),
|
||||
MultiPolygon(nil),
|
||||
Collection(nil),
|
||||
|
||||
// Collection of Collection
|
||||
Collection{Collection{Point{}}},
|
||||
}
|
||||
|
||||
// A Collection is a collection of geometries that is also a Geometry.
|
||||
type Collection []Geometry
|
||||
|
||||
// GeoJSONType returns the geometry collection type.
|
||||
func (c Collection) GeoJSONType() string {
|
||||
return "GeometryCollection"
|
||||
}
|
||||
|
||||
// Dimensions returns the max of the dimensions of the collection.
|
||||
func (c Collection) Dimensions() int {
|
||||
max := -1
|
||||
for _, g := range c {
|
||||
if d := g.Dimensions(); d > max {
|
||||
max = d
|
||||
}
|
||||
}
|
||||
|
||||
return max
|
||||
}
|
||||
|
||||
// Bound returns the bounding box of all the Geometries combined.
|
||||
func (c Collection) Bound() Bound {
|
||||
if len(c) == 0 {
|
||||
return emptyBound
|
||||
}
|
||||
|
||||
var b Bound
|
||||
start := -1
|
||||
|
||||
for i, g := range c {
|
||||
if g != nil {
|
||||
start = i
|
||||
b = g.Bound()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if start == -1 {
|
||||
return emptyBound
|
||||
}
|
||||
|
||||
for i := start + 1; i < len(c); i++ {
|
||||
if c[i] == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
b = b.Union(c[i].Bound())
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Equal compares two collections. Returns true if lengths are the same
|
||||
// and all the sub geometries are the same and in the same order.
|
||||
func (c Collection) Equal(collection Collection) bool {
|
||||
if len(c) != len(collection) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, g := range c {
|
||||
if !Equal(g, collection[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Clone returns a deep copy of the collection.
|
||||
func (c Collection) Clone() Collection {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
nc := make(Collection, len(c))
|
||||
for i, g := range c {
|
||||
nc[i] = Clone(g)
|
||||
}
|
||||
|
||||
return nc
|
||||
}
|
||||
71
vendor/github.com/paulmach/orb/internal/length/length.go
generated
vendored
Normal file
71
vendor/github.com/paulmach/orb/internal/length/length.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
package length
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/paulmach/orb"
|
||||
)
|
||||
|
||||
// Length returns the length of the boundary of the geometry
|
||||
// using 2d euclidean geometry.
|
||||
func Length(g orb.Geometry, df orb.DistanceFunc) float64 {
|
||||
if g == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
switch g := g.(type) {
|
||||
case orb.Point:
|
||||
return 0
|
||||
case orb.MultiPoint:
|
||||
return 0
|
||||
case orb.LineString:
|
||||
return lineStringLength(g, df)
|
||||
case orb.MultiLineString:
|
||||
sum := 0.0
|
||||
for _, ls := range g {
|
||||
sum += lineStringLength(ls, df)
|
||||
}
|
||||
|
||||
return sum
|
||||
case orb.Ring:
|
||||
return lineStringLength(orb.LineString(g), df)
|
||||
case orb.Polygon:
|
||||
return polygonLength(g, df)
|
||||
case orb.MultiPolygon:
|
||||
sum := 0.0
|
||||
for _, p := range g {
|
||||
sum += polygonLength(p, df)
|
||||
}
|
||||
|
||||
return sum
|
||||
case orb.Collection:
|
||||
sum := 0.0
|
||||
for _, c := range g {
|
||||
sum += Length(c, df)
|
||||
}
|
||||
|
||||
return sum
|
||||
case orb.Bound:
|
||||
return Length(g.ToRing(), df)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("geometry type not supported: %T", g))
|
||||
}
|
||||
|
||||
func lineStringLength(ls orb.LineString, df orb.DistanceFunc) float64 {
|
||||
sum := 0.0
|
||||
for i := 1; i < len(ls); i++ {
|
||||
sum += df(ls[i], ls[i-1])
|
||||
}
|
||||
|
||||
return sum
|
||||
}
|
||||
|
||||
func polygonLength(p orb.Polygon, df orb.DistanceFunc) float64 {
|
||||
sum := 0.0
|
||||
for _, r := range p {
|
||||
sum += lineStringLength(orb.LineString(r), df)
|
||||
}
|
||||
|
||||
return sum
|
||||
}
|
||||
40
vendor/github.com/paulmach/orb/line_string.go
generated
vendored
Normal file
40
vendor/github.com/paulmach/orb/line_string.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package orb
|
||||
|
||||
// LineString represents a set of points to be thought of as a polyline.
|
||||
type LineString []Point
|
||||
|
||||
// GeoJSONType returns the GeoJSON type for the object.
|
||||
func (ls LineString) GeoJSONType() string {
|
||||
return "LineString"
|
||||
}
|
||||
|
||||
// Dimensions returns 1 because a LineString is a 1d object.
|
||||
func (ls LineString) Dimensions() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Reverse will reverse the line string.
|
||||
// This is done inplace, ie. it modifies the original data.
|
||||
func (ls LineString) Reverse() {
|
||||
l := len(ls) - 1
|
||||
for i := 0; i <= l/2; i++ {
|
||||
ls[i], ls[l-i] = ls[l-i], ls[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Bound returns a rect around the line string. Uses rectangular coordinates.
|
||||
func (ls LineString) Bound() Bound {
|
||||
return MultiPoint(ls).Bound()
|
||||
}
|
||||
|
||||
// Equal compares two line strings. Returns true if lengths are the same
|
||||
// and all points are Equal.
|
||||
func (ls LineString) Equal(lineString LineString) bool {
|
||||
return MultiPoint(ls).Equal(MultiPoint(lineString))
|
||||
}
|
||||
|
||||
// Clone returns a new copy of the line string.
|
||||
func (ls LineString) Clone() LineString {
|
||||
ps := MultiPoint(ls)
|
||||
return LineString(ps.Clone())
|
||||
}
|
||||
58
vendor/github.com/paulmach/orb/multi_line_string.go
generated
vendored
Normal file
58
vendor/github.com/paulmach/orb/multi_line_string.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
package orb
|
||||
|
||||
// MultiLineString is a set of polylines.
|
||||
type MultiLineString []LineString
|
||||
|
||||
// GeoJSONType returns the GeoJSON type for the object.
|
||||
func (mls MultiLineString) GeoJSONType() string {
|
||||
return "MultiLineString"
|
||||
}
|
||||
|
||||
// Dimensions returns 1 because a MultiLineString is a 2d object.
|
||||
func (mls MultiLineString) Dimensions() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Bound returns a bound around all the line strings.
|
||||
func (mls MultiLineString) Bound() Bound {
|
||||
if len(mls) == 0 {
|
||||
return emptyBound
|
||||
}
|
||||
|
||||
bound := mls[0].Bound()
|
||||
for i := 1; i < len(mls); i++ {
|
||||
bound = bound.Union(mls[i].Bound())
|
||||
}
|
||||
|
||||
return bound
|
||||
}
|
||||
|
||||
// Equal compares two multi line strings. Returns true if lengths are the same
|
||||
// and all points are Equal.
|
||||
func (mls MultiLineString) Equal(multiLineString MultiLineString) bool {
|
||||
if len(mls) != len(multiLineString) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, ls := range mls {
|
||||
if !ls.Equal(multiLineString[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Clone returns a new deep copy of the multi line string.
|
||||
func (mls MultiLineString) Clone() MultiLineString {
|
||||
if mls == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
nmls := make(MultiLineString, 0, len(mls))
|
||||
for _, ls := range mls {
|
||||
nmls = append(nmls, ls.Clone())
|
||||
}
|
||||
|
||||
return nmls
|
||||
}
|
||||
56
vendor/github.com/paulmach/orb/multi_point.go
generated
vendored
Normal file
56
vendor/github.com/paulmach/orb/multi_point.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
package orb
|
||||
|
||||
// A MultiPoint represents a set of points in the 2D Eucledian or Cartesian plane.
|
||||
type MultiPoint []Point
|
||||
|
||||
// GeoJSONType returns the GeoJSON type for the object.
|
||||
func (mp MultiPoint) GeoJSONType() string {
|
||||
return "MultiPoint"
|
||||
}
|
||||
|
||||
// Dimensions returns 0 because a MultiPoint is a 0d object.
|
||||
func (mp MultiPoint) Dimensions() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Clone returns a new copy of the points.
|
||||
func (mp MultiPoint) Clone() MultiPoint {
|
||||
if mp == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
points := make([]Point, len(mp))
|
||||
copy(points, mp)
|
||||
|
||||
return MultiPoint(points)
|
||||
}
|
||||
|
||||
// Bound returns a bound around the points. Uses rectangular coordinates.
|
||||
func (mp MultiPoint) Bound() Bound {
|
||||
if len(mp) == 0 {
|
||||
return emptyBound
|
||||
}
|
||||
|
||||
b := Bound{mp[0], mp[0]}
|
||||
for _, p := range mp {
|
||||
b = b.Extend(p)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Equal compares two MultiPoint objects. Returns true if lengths are the same
|
||||
// and all points are Equal, and in the same order.
|
||||
func (mp MultiPoint) Equal(multiPoint MultiPoint) bool {
|
||||
if len(mp) != len(multiPoint) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range mp {
|
||||
if !mp[i].Equal(multiPoint[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
56
vendor/github.com/paulmach/orb/multi_polygon.go
generated
vendored
Normal file
56
vendor/github.com/paulmach/orb/multi_polygon.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
package orb
|
||||
|
||||
// MultiPolygon is a set of polygons.
|
||||
type MultiPolygon []Polygon
|
||||
|
||||
// GeoJSONType returns the GeoJSON type for the object.
|
||||
func (mp MultiPolygon) GeoJSONType() string {
|
||||
return "MultiPolygon"
|
||||
}
|
||||
|
||||
// Dimensions returns 2 because a MultiPolygon is a 2d object.
|
||||
func (mp MultiPolygon) Dimensions() int {
|
||||
return 2
|
||||
}
|
||||
|
||||
// Bound returns a bound around the multi-polygon.
|
||||
func (mp MultiPolygon) Bound() Bound {
|
||||
if len(mp) == 0 {
|
||||
return emptyBound
|
||||
}
|
||||
bound := mp[0].Bound()
|
||||
for i := 1; i < len(mp); i++ {
|
||||
bound = bound.Union(mp[i].Bound())
|
||||
}
|
||||
|
||||
return bound
|
||||
}
|
||||
|
||||
// Equal compares two multi-polygons.
|
||||
func (mp MultiPolygon) Equal(multiPolygon MultiPolygon) bool {
|
||||
if len(mp) != len(multiPolygon) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, p := range mp {
|
||||
if !p.Equal(multiPolygon[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Clone returns a new deep copy of the multi-polygon.
|
||||
func (mp MultiPolygon) Clone() MultiPolygon {
|
||||
if mp == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
nmp := make(MultiPolygon, 0, len(mp))
|
||||
for _, p := range mp {
|
||||
nmp = append(nmp, p.Clone())
|
||||
}
|
||||
|
||||
return nmp
|
||||
}
|
||||
40
vendor/github.com/paulmach/orb/planar/README.md
generated
vendored
Normal file
40
vendor/github.com/paulmach/orb/planar/README.md
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
# orb/planar [](https://pkg.go.dev/github.com/paulmach/orb/planar)
|
||||
|
||||
The geometries defined in the `orb` package are generic 2d geometries.
|
||||
Depending on what projection they're in, e.g. lon/lat or flat on the plane,
|
||||
area and distance calculations are different. This package implements methods
|
||||
that assume the planar or Euclidean context.
|
||||
|
||||
## Examples
|
||||
|
||||
Area of 3-4-5 triangle:
|
||||
|
||||
```go
|
||||
r := orb.Ring{{0, 0}, {3, 0}, {0, 4}, {0, 0}}
|
||||
a := planar.Area(r)
|
||||
|
||||
fmt.Println(a)
|
||||
// Output:
|
||||
// 6
|
||||
```
|
||||
|
||||
Distance between two points:
|
||||
|
||||
```go
|
||||
d := planar.Distance(orb.Point{0, 0}, orb.Point{3, 4})
|
||||
|
||||
fmt.Println(d)
|
||||
// Output:
|
||||
// 5
|
||||
```
|
||||
|
||||
Length/circumference of a 3-4-5 triangle:
|
||||
|
||||
```go
|
||||
r := orb.Ring{{0, 0}, {3, 0}, {0, 4}, {0, 0}}
|
||||
l := planar.Length(r)
|
||||
|
||||
fmt.Println(l)
|
||||
// Output:
|
||||
// 12
|
||||
```
|
||||
284
vendor/github.com/paulmach/orb/planar/area.go
generated
vendored
Normal file
284
vendor/github.com/paulmach/orb/planar/area.go
generated
vendored
Normal file
@@ -0,0 +1,284 @@
|
||||
// Package planar computes properties on geometries assuming they are
|
||||
// in 2d euclidean space.
|
||||
package planar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/paulmach/orb"
|
||||
)
|
||||
|
||||
// Area returns the area of the geometry in the 2d plane.
|
||||
func Area(g orb.Geometry) float64 {
|
||||
// TODO: make faster non-centroid version.
|
||||
_, a := CentroidArea(g)
|
||||
return a
|
||||
}
|
||||
|
||||
// CentroidArea returns both the centroid and the area in the 2d plane.
|
||||
// Since the area is need for the centroid, return both.
|
||||
// Polygon area will always be >= zero. Ring area my be negative if it has
|
||||
// a clockwise winding orider.
|
||||
func CentroidArea(g orb.Geometry) (orb.Point, float64) {
|
||||
if g == nil {
|
||||
return orb.Point{}, 0
|
||||
}
|
||||
|
||||
switch g := g.(type) {
|
||||
case orb.Point:
|
||||
return multiPointCentroid(orb.MultiPoint{g}), 0
|
||||
case orb.MultiPoint:
|
||||
return multiPointCentroid(g), 0
|
||||
case orb.LineString:
|
||||
return multiLineStringCentroid(orb.MultiLineString{g}), 0
|
||||
case orb.MultiLineString:
|
||||
return multiLineStringCentroid(g), 0
|
||||
case orb.Ring:
|
||||
return ringCentroidArea(g)
|
||||
case orb.Polygon:
|
||||
return polygonCentroidArea(g)
|
||||
case orb.MultiPolygon:
|
||||
return multiPolygonCentroidArea(g)
|
||||
case orb.Collection:
|
||||
return collectionCentroidArea(g)
|
||||
case orb.Bound:
|
||||
return CentroidArea(g.ToRing())
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("geometry type not supported: %T", g))
|
||||
}
|
||||
|
||||
func multiPointCentroid(mp orb.MultiPoint) orb.Point {
|
||||
if len(mp) == 0 {
|
||||
return orb.Point{}
|
||||
}
|
||||
|
||||
x, y := 0.0, 0.0
|
||||
for _, p := range mp {
|
||||
x += p[0]
|
||||
y += p[1]
|
||||
}
|
||||
|
||||
num := float64(len(mp))
|
||||
return orb.Point{x / num, y / num}
|
||||
}
|
||||
|
||||
func multiLineStringCentroid(mls orb.MultiLineString) orb.Point {
|
||||
point := orb.Point{}
|
||||
dist := 0.0
|
||||
|
||||
if len(mls) == 0 {
|
||||
return orb.Point{}
|
||||
}
|
||||
|
||||
validCount := 0
|
||||
for _, ls := range mls {
|
||||
c, d := lineStringCentroidDist(ls)
|
||||
if d == math.Inf(1) {
|
||||
continue
|
||||
}
|
||||
|
||||
dist += d
|
||||
validCount++
|
||||
|
||||
if d == 0 {
|
||||
d = 1.0
|
||||
}
|
||||
|
||||
point[0] += c[0] * d
|
||||
point[1] += c[1] * d
|
||||
}
|
||||
|
||||
if validCount == 0 {
|
||||
return orb.Point{}
|
||||
}
|
||||
|
||||
if dist == math.Inf(1) || dist == 0.0 {
|
||||
point[0] /= float64(validCount)
|
||||
point[1] /= float64(validCount)
|
||||
return point
|
||||
}
|
||||
|
||||
point[0] /= dist
|
||||
point[1] /= dist
|
||||
|
||||
return point
|
||||
}
|
||||
|
||||
func lineStringCentroidDist(ls orb.LineString) (orb.Point, float64) {
|
||||
dist := 0.0
|
||||
point := orb.Point{}
|
||||
|
||||
if len(ls) == 0 {
|
||||
return orb.Point{}, math.Inf(1)
|
||||
}
|
||||
|
||||
// implicitly move everything to near the origin to help with roundoff
|
||||
offset := ls[0]
|
||||
for i := 0; i < len(ls)-1; i++ {
|
||||
p1 := orb.Point{
|
||||
ls[i][0] - offset[0],
|
||||
ls[i][1] - offset[1],
|
||||
}
|
||||
|
||||
p2 := orb.Point{
|
||||
ls[i+1][0] - offset[0],
|
||||
ls[i+1][1] - offset[1],
|
||||
}
|
||||
|
||||
d := Distance(p1, p2)
|
||||
|
||||
point[0] += (p1[0] + p2[0]) / 2.0 * d
|
||||
point[1] += (p1[1] + p2[1]) / 2.0 * d
|
||||
dist += d
|
||||
}
|
||||
|
||||
if dist == 0 {
|
||||
return ls[0], 0
|
||||
}
|
||||
|
||||
point[0] /= dist
|
||||
point[1] /= dist
|
||||
|
||||
point[0] += ls[0][0]
|
||||
point[1] += ls[0][1]
|
||||
return point, dist
|
||||
}
|
||||
|
||||
func ringCentroidArea(r orb.Ring) (orb.Point, float64) {
|
||||
centroid := orb.Point{}
|
||||
area := 0.0
|
||||
|
||||
if len(r) == 0 {
|
||||
return orb.Point{}, 0
|
||||
}
|
||||
|
||||
// implicitly move everything to near the origin to help with roundoff
|
||||
offsetX := r[0][0]
|
||||
offsetY := r[0][1]
|
||||
for i := 1; i < len(r)-1; i++ {
|
||||
a := (r[i][0]-offsetX)*(r[i+1][1]-offsetY) -
|
||||
(r[i+1][0]-offsetX)*(r[i][1]-offsetY)
|
||||
area += a
|
||||
|
||||
centroid[0] += (r[i][0] + r[i+1][0] - 2*offsetX) * a
|
||||
centroid[1] += (r[i][1] + r[i+1][1] - 2*offsetY) * a
|
||||
}
|
||||
|
||||
if area == 0 {
|
||||
return r[0], 0
|
||||
}
|
||||
|
||||
// no need to deal with first and last vertex since we "moved"
|
||||
// that point the origin (multiply by 0 == 0)
|
||||
|
||||
area /= 2
|
||||
centroid[0] /= 6 * area
|
||||
centroid[1] /= 6 * area
|
||||
|
||||
centroid[0] += offsetX
|
||||
centroid[1] += offsetY
|
||||
|
||||
return centroid, area
|
||||
}
|
||||
|
||||
func polygonCentroidArea(p orb.Polygon) (orb.Point, float64) {
|
||||
if len(p) == 0 {
|
||||
return orb.Point{}, 0
|
||||
}
|
||||
|
||||
centroid, area := ringCentroidArea(p[0])
|
||||
area = math.Abs(area)
|
||||
if len(p) == 1 {
|
||||
if area == 0 {
|
||||
c, _ := lineStringCentroidDist(orb.LineString(p[0]))
|
||||
return c, 0
|
||||
}
|
||||
return centroid, area
|
||||
}
|
||||
|
||||
holeArea := 0.0
|
||||
weightedHoleCentroid := orb.Point{}
|
||||
for i := 1; i < len(p); i++ {
|
||||
hc, ha := ringCentroidArea(p[i])
|
||||
ha = math.Abs(ha)
|
||||
|
||||
holeArea += ha
|
||||
weightedHoleCentroid[0] += hc[0] * ha
|
||||
weightedHoleCentroid[1] += hc[1] * ha
|
||||
}
|
||||
|
||||
totalArea := area - holeArea
|
||||
if totalArea == 0 {
|
||||
c, _ := lineStringCentroidDist(orb.LineString(p[0]))
|
||||
return c, 0
|
||||
}
|
||||
|
||||
centroid[0] = (area*centroid[0] - weightedHoleCentroid[0]) / totalArea
|
||||
centroid[1] = (area*centroid[1] - weightedHoleCentroid[1]) / totalArea
|
||||
|
||||
return centroid, totalArea
|
||||
}
|
||||
|
||||
func multiPolygonCentroidArea(mp orb.MultiPolygon) (orb.Point, float64) {
|
||||
point := orb.Point{}
|
||||
area := 0.0
|
||||
|
||||
for _, p := range mp {
|
||||
c, a := polygonCentroidArea(p)
|
||||
|
||||
point[0] += c[0] * a
|
||||
point[1] += c[1] * a
|
||||
|
||||
area += a
|
||||
}
|
||||
|
||||
if area == 0 {
|
||||
return orb.Point{}, 0
|
||||
}
|
||||
|
||||
point[0] /= area
|
||||
point[1] /= area
|
||||
|
||||
return point, area
|
||||
}
|
||||
|
||||
func collectionCentroidArea(c orb.Collection) (orb.Point, float64) {
|
||||
point := orb.Point{}
|
||||
area := 0.0
|
||||
|
||||
max := maxDim(c)
|
||||
for _, g := range c {
|
||||
if g.Dimensions() != max {
|
||||
continue
|
||||
}
|
||||
|
||||
c, a := CentroidArea(g)
|
||||
|
||||
point[0] += c[0] * a
|
||||
point[1] += c[1] * a
|
||||
|
||||
area += a
|
||||
}
|
||||
|
||||
if area == 0 {
|
||||
return orb.Point{}, 0
|
||||
}
|
||||
|
||||
point[0] /= area
|
||||
point[1] /= area
|
||||
|
||||
return point, area
|
||||
}
|
||||
|
||||
func maxDim(c orb.Collection) int {
|
||||
max := 0
|
||||
for _, g := range c {
|
||||
if d := g.Dimensions(); d > max {
|
||||
max = d
|
||||
}
|
||||
}
|
||||
|
||||
return max
|
||||
}
|
||||
122
vendor/github.com/paulmach/orb/planar/contains.go
generated
vendored
Normal file
122
vendor/github.com/paulmach/orb/planar/contains.go
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
package planar
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/paulmach/orb"
|
||||
)
|
||||
|
||||
// RingContains returns true if the point is inside the ring.
|
||||
// Points on the boundary are considered in.
|
||||
func RingContains(r orb.Ring, point orb.Point) bool {
|
||||
if !r.Bound().Contains(point) {
|
||||
return false
|
||||
}
|
||||
|
||||
c, on := rayIntersect(point, r[0], r[len(r)-1])
|
||||
if on {
|
||||
return true
|
||||
}
|
||||
|
||||
for i := 0; i < len(r)-1; i++ {
|
||||
inter, on := rayIntersect(point, r[i], r[i+1])
|
||||
if on {
|
||||
return true
|
||||
}
|
||||
|
||||
if inter {
|
||||
c = !c
|
||||
}
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// PolygonContains checks if the point is within the polygon.
|
||||
// Points on the boundary are considered in.
|
||||
func PolygonContains(p orb.Polygon, point orb.Point) bool {
|
||||
if !RingContains(p[0], point) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 1; i < len(p); i++ {
|
||||
if RingContains(p[i], point) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// MultiPolygonContains checks if the point is within the multi-polygon.
|
||||
// Points on the boundary are considered in.
|
||||
func MultiPolygonContains(mp orb.MultiPolygon, point orb.Point) bool {
|
||||
for _, p := range mp {
|
||||
if PolygonContains(p, point) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Original implementation: http://rosettacode.org/wiki/Ray-casting_algorithm#Go
|
||||
func rayIntersect(p, s, e orb.Point) (intersects, on bool) {
|
||||
if s[0] > e[0] {
|
||||
s, e = e, s
|
||||
}
|
||||
|
||||
if p[0] == s[0] {
|
||||
if p[1] == s[1] {
|
||||
// p == start
|
||||
return false, true
|
||||
} else if s[0] == e[0] {
|
||||
// vertical segment (s -> e)
|
||||
// return true if within the line, check to see if start or end is greater.
|
||||
if s[1] > e[1] && s[1] >= p[1] && p[1] >= e[1] {
|
||||
return false, true
|
||||
}
|
||||
|
||||
if e[1] > s[1] && e[1] >= p[1] && p[1] >= s[1] {
|
||||
return false, true
|
||||
}
|
||||
}
|
||||
|
||||
// Move the y coordinate to deal with degenerate case
|
||||
p[0] = math.Nextafter(p[0], math.Inf(1))
|
||||
} else if p[0] == e[0] {
|
||||
if p[1] == e[1] {
|
||||
// matching the end point
|
||||
return false, true
|
||||
}
|
||||
|
||||
p[0] = math.Nextafter(p[0], math.Inf(1))
|
||||
}
|
||||
|
||||
if p[0] < s[0] || p[0] > e[0] {
|
||||
return false, false
|
||||
}
|
||||
|
||||
if s[1] > e[1] {
|
||||
if p[1] > s[1] {
|
||||
return false, false
|
||||
} else if p[1] < e[1] {
|
||||
return true, false
|
||||
}
|
||||
} else {
|
||||
if p[1] > e[1] {
|
||||
return false, false
|
||||
} else if p[1] < s[1] {
|
||||
return true, false
|
||||
}
|
||||
}
|
||||
|
||||
rs := (p[1] - s[1]) / (p[0] - s[0])
|
||||
ds := (e[1] - s[1]) / (e[0] - s[0])
|
||||
|
||||
if rs == ds {
|
||||
return false, true
|
||||
}
|
||||
|
||||
return rs <= ds, false
|
||||
}
|
||||
21
vendor/github.com/paulmach/orb/planar/distance.go
generated
vendored
Normal file
21
vendor/github.com/paulmach/orb/planar/distance.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package planar
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/paulmach/orb"
|
||||
)
|
||||
|
||||
// Distance returns the distance between two points in 2d euclidean geometry.
|
||||
func Distance(p1, p2 orb.Point) float64 {
|
||||
d0 := (p1[0] - p2[0])
|
||||
d1 := (p1[1] - p2[1])
|
||||
return math.Sqrt(d0*d0 + d1*d1)
|
||||
}
|
||||
|
||||
// DistanceSquared returns the square of the distance between two points in 2d euclidean geometry.
|
||||
func DistanceSquared(p1, p2 orb.Point) float64 {
|
||||
d0 := (p1[0] - p2[0])
|
||||
d1 := (p1[1] - p2[1])
|
||||
return d0*d0 + d1*d1
|
||||
}
|
||||
173
vendor/github.com/paulmach/orb/planar/distance_from.go
generated
vendored
Normal file
173
vendor/github.com/paulmach/orb/planar/distance_from.go
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
package planar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/paulmach/orb"
|
||||
)
|
||||
|
||||
// DistanceFromSegment returns the point's distance from the segment [a, b].
|
||||
func DistanceFromSegment(a, b, point orb.Point) float64 {
|
||||
return math.Sqrt(DistanceFromSegmentSquared(a, b, point))
|
||||
}
|
||||
|
||||
// DistanceFromSegmentSquared returns point's squared distance from the segement [a, b].
|
||||
func DistanceFromSegmentSquared(a, b, point orb.Point) float64 {
|
||||
x := a[0]
|
||||
y := a[1]
|
||||
dx := b[0] - x
|
||||
dy := b[1] - y
|
||||
|
||||
if dx != 0 || dy != 0 {
|
||||
t := ((point[0]-x)*dx + (point[1]-y)*dy) / (dx*dx + dy*dy)
|
||||
|
||||
if t > 1 {
|
||||
x = b[0]
|
||||
y = b[1]
|
||||
} else if t > 0 {
|
||||
x += dx * t
|
||||
y += dy * t
|
||||
}
|
||||
}
|
||||
|
||||
dx = point[0] - x
|
||||
dy = point[1] - y
|
||||
|
||||
return dx*dx + dy*dy
|
||||
}
|
||||
|
||||
// DistanceFrom returns the distance from the boundary of the geometry in
|
||||
// the units of the geometry.
|
||||
func DistanceFrom(g orb.Geometry, p orb.Point) float64 {
|
||||
d, _ := DistanceFromWithIndex(g, p)
|
||||
return d
|
||||
}
|
||||
|
||||
// DistanceFromWithIndex returns the minimum euclidean distance
|
||||
// from the boundary of the geometry plus the index of the sub-geometry
|
||||
// that was the match.
|
||||
func DistanceFromWithIndex(g orb.Geometry, p orb.Point) (float64, int) {
|
||||
if g == nil {
|
||||
return math.Inf(1), -1
|
||||
}
|
||||
|
||||
switch g := g.(type) {
|
||||
case orb.Point:
|
||||
return Distance(g, p), 0
|
||||
case orb.MultiPoint:
|
||||
return multiPointDistanceFrom(g, p)
|
||||
case orb.LineString:
|
||||
return lineStringDistanceFrom(g, p)
|
||||
case orb.MultiLineString:
|
||||
dist := math.Inf(1)
|
||||
index := -1
|
||||
for i, ls := range g {
|
||||
if d, _ := lineStringDistanceFrom(ls, p); d < dist {
|
||||
dist = d
|
||||
index = i
|
||||
}
|
||||
}
|
||||
|
||||
return dist, index
|
||||
case orb.Ring:
|
||||
return lineStringDistanceFrom(orb.LineString(g), p)
|
||||
case orb.Polygon:
|
||||
return polygonDistanceFrom(g, p)
|
||||
case orb.MultiPolygon:
|
||||
dist := math.Inf(1)
|
||||
index := -1
|
||||
for i, poly := range g {
|
||||
if d, _ := polygonDistanceFrom(poly, p); d < dist {
|
||||
dist = d
|
||||
index = i
|
||||
}
|
||||
}
|
||||
|
||||
return dist, index
|
||||
case orb.Collection:
|
||||
dist := math.Inf(1)
|
||||
index := -1
|
||||
for i, ge := range g {
|
||||
if d, _ := DistanceFromWithIndex(ge, p); d < dist {
|
||||
dist = d
|
||||
index = i
|
||||
}
|
||||
}
|
||||
|
||||
return dist, index
|
||||
case orb.Bound:
|
||||
return DistanceFromWithIndex(g.ToRing(), p)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("geometry type not supported: %T", g))
|
||||
}
|
||||
|
||||
func multiPointDistanceFrom(mp orb.MultiPoint, p orb.Point) (float64, int) {
|
||||
dist := math.Inf(1)
|
||||
index := -1
|
||||
|
||||
for i := range mp {
|
||||
if d := DistanceSquared(mp[i], p); d < dist {
|
||||
dist = d
|
||||
index = i
|
||||
}
|
||||
}
|
||||
|
||||
return math.Sqrt(dist), index
|
||||
}
|
||||
|
||||
func lineStringDistanceFrom(ls orb.LineString, p orb.Point) (float64, int) {
|
||||
dist := math.Inf(1)
|
||||
index := -1
|
||||
|
||||
for i := 0; i < len(ls)-1; i++ {
|
||||
if d := segmentDistanceFromSquared(ls[i], ls[i+1], p); d < dist {
|
||||
dist = d
|
||||
index = i
|
||||
}
|
||||
}
|
||||
|
||||
return math.Sqrt(dist), index
|
||||
}
|
||||
|
||||
func polygonDistanceFrom(p orb.Polygon, point orb.Point) (float64, int) {
|
||||
if len(p) == 0 {
|
||||
return math.Inf(1), -1
|
||||
}
|
||||
|
||||
dist, index := lineStringDistanceFrom(orb.LineString(p[0]), point)
|
||||
for i := 1; i < len(p); i++ {
|
||||
d, i := lineStringDistanceFrom(orb.LineString(p[i]), point)
|
||||
if d < dist {
|
||||
dist = d
|
||||
index = i
|
||||
}
|
||||
}
|
||||
|
||||
return dist, index
|
||||
}
|
||||
|
||||
func segmentDistanceFromSquared(p1, p2, point orb.Point) float64 {
|
||||
x := p1[0]
|
||||
y := p1[1]
|
||||
dx := p2[0] - x
|
||||
dy := p2[1] - y
|
||||
|
||||
if dx != 0 || dy != 0 {
|
||||
t := ((point[0]-x)*dx + (point[1]-y)*dy) / (dx*dx + dy*dy)
|
||||
|
||||
if t > 1 {
|
||||
x = p2[0]
|
||||
y = p2[1]
|
||||
} else if t > 0 {
|
||||
x += dx * t
|
||||
y += dy * t
|
||||
}
|
||||
}
|
||||
|
||||
dx = point[0] - x
|
||||
dy = point[1] - y
|
||||
|
||||
return dx*dx + dy*dy
|
||||
}
|
||||
12
vendor/github.com/paulmach/orb/planar/length.go
generated
vendored
Normal file
12
vendor/github.com/paulmach/orb/planar/length.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
package planar
|
||||
|
||||
import (
|
||||
"github.com/paulmach/orb"
|
||||
"github.com/paulmach/orb/internal/length"
|
||||
)
|
||||
|
||||
// Length returns the length of the boundary of the geometry
|
||||
// using 2d euclidean geometry.
|
||||
func Length(g orb.Geometry) float64 {
|
||||
return length.Length(g, Distance)
|
||||
}
|
||||
51
vendor/github.com/paulmach/orb/point.go
generated
vendored
Normal file
51
vendor/github.com/paulmach/orb/point.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package orb
|
||||
|
||||
// A Point is a Lon/Lat 2d point.
|
||||
type Point [2]float64
|
||||
|
||||
var _ Pointer = Point{}
|
||||
|
||||
// GeoJSONType returns the GeoJSON type for the object.
|
||||
func (p Point) GeoJSONType() string {
|
||||
return "Point"
|
||||
}
|
||||
|
||||
// Dimensions returns 0 because a point is a 0d object.
|
||||
func (p Point) Dimensions() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Bound returns a single point bound of the point.
|
||||
func (p Point) Bound() Bound {
|
||||
return Bound{p, p}
|
||||
}
|
||||
|
||||
// Point returns itself so it implements the Pointer interface.
|
||||
func (p Point) Point() Point {
|
||||
return p
|
||||
}
|
||||
|
||||
// Y returns the vertical coordinate of the point.
|
||||
func (p Point) Y() float64 {
|
||||
return p[1]
|
||||
}
|
||||
|
||||
// X returns the horizontal coordinate of the point.
|
||||
func (p Point) X() float64 {
|
||||
return p[0]
|
||||
}
|
||||
|
||||
// Lat returns the vertical, latitude coordinate of the point.
|
||||
func (p Point) Lat() float64 {
|
||||
return p[1]
|
||||
}
|
||||
|
||||
// Lon returns the horizontal, longitude coordinate of the point.
|
||||
func (p Point) Lon() float64 {
|
||||
return p[0]
|
||||
}
|
||||
|
||||
// Equal checks if the point represents the same point or vector.
|
||||
func (p Point) Equal(point Point) bool {
|
||||
return p[0] == point[0] && p[1] == point[1]
|
||||
}
|
||||
55
vendor/github.com/paulmach/orb/polygon.go
generated
vendored
Normal file
55
vendor/github.com/paulmach/orb/polygon.go
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
package orb
|
||||
|
||||
// Polygon is a closed area. The first LineString is the outer ring.
|
||||
// The others are the holes. Each LineString is expected to be closed
|
||||
// ie. the first point matches the last.
|
||||
type Polygon []Ring
|
||||
|
||||
// GeoJSONType returns the GeoJSON type for the object.
|
||||
func (p Polygon) GeoJSONType() string {
|
||||
return "Polygon"
|
||||
}
|
||||
|
||||
// Dimensions returns 2 because a Polygon is a 2d object.
|
||||
func (p Polygon) Dimensions() int {
|
||||
return 2
|
||||
}
|
||||
|
||||
// Bound returns a bound around the polygon.
|
||||
func (p Polygon) Bound() Bound {
|
||||
if len(p) == 0 {
|
||||
return emptyBound
|
||||
}
|
||||
return p[0].Bound()
|
||||
}
|
||||
|
||||
// Equal compares two polygons. Returns true if lengths are the same
|
||||
// and all points are Equal.
|
||||
func (p Polygon) Equal(polygon Polygon) bool {
|
||||
if len(p) != len(polygon) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range p {
|
||||
if !p[i].Equal(polygon[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Clone returns a new deep copy of the polygon.
|
||||
// All of the rings are also cloned.
|
||||
func (p Polygon) Clone() Polygon {
|
||||
if p == nil {
|
||||
return p
|
||||
}
|
||||
|
||||
np := make(Polygon, 0, len(p))
|
||||
for _, r := range p {
|
||||
np = append(np, r.Clone())
|
||||
}
|
||||
|
||||
return np
|
||||
}
|
||||
75
vendor/github.com/paulmach/orb/ring.go
generated
vendored
Normal file
75
vendor/github.com/paulmach/orb/ring.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
package orb
|
||||
|
||||
// Ring represents a set of ring on the earth.
|
||||
type Ring LineString
|
||||
|
||||
// GeoJSONType returns the GeoJSON type for the object.
|
||||
func (r Ring) GeoJSONType() string {
|
||||
return "Polygon"
|
||||
}
|
||||
|
||||
// Dimensions returns 2 because a Ring is a 2d object.
|
||||
func (r Ring) Dimensions() int {
|
||||
return 2
|
||||
}
|
||||
|
||||
// Closed will return true if the ring is a real ring.
|
||||
// ie. 4+ points and the first and last points match.
|
||||
// NOTE: this will not check for self-intersection.
|
||||
func (r Ring) Closed() bool {
|
||||
return (len(r) >= 4) && (r[0] == r[len(r)-1])
|
||||
}
|
||||
|
||||
// Reverse changes the direction of the ring.
|
||||
// This is done inplace, ie. it modifies the original data.
|
||||
func (r Ring) Reverse() {
|
||||
LineString(r).Reverse()
|
||||
}
|
||||
|
||||
// Bound returns a rect around the ring. Uses rectangular coordinates.
|
||||
func (r Ring) Bound() Bound {
|
||||
return MultiPoint(r).Bound()
|
||||
}
|
||||
|
||||
// Orientation returns 1 if the the ring is in couter-clockwise order,
|
||||
// return -1 if the ring is the clockwise order and 0 if the ring is
|
||||
// degenerate and had no area.
|
||||
func (r Ring) Orientation() Orientation {
|
||||
area := 0.0
|
||||
|
||||
// This is a fast planar area computation, which is okay for this use.
|
||||
// implicitly move everything to near the origin to help with roundoff
|
||||
offsetX := r[0][0]
|
||||
offsetY := r[0][1]
|
||||
for i := 1; i < len(r)-1; i++ {
|
||||
area += (r[i][0]-offsetX)*(r[i+1][1]-offsetY) -
|
||||
(r[i+1][0]-offsetX)*(r[i][1]-offsetY)
|
||||
}
|
||||
|
||||
if area > 0 {
|
||||
return CCW
|
||||
}
|
||||
|
||||
if area < 0 {
|
||||
return CW
|
||||
}
|
||||
|
||||
// degenerate case, no area
|
||||
return 0
|
||||
}
|
||||
|
||||
// Equal compares two rings. Returns true if lengths are the same
|
||||
// and all points are Equal.
|
||||
func (r Ring) Equal(ring Ring) bool {
|
||||
return MultiPoint(r).Equal(MultiPoint(ring))
|
||||
}
|
||||
|
||||
// Clone returns a new copy of the ring.
|
||||
func (r Ring) Clone() Ring {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ps := MultiPoint(r)
|
||||
return Ring(ps.Clone())
|
||||
}
|
||||
100
vendor/github.com/paulmach/orb/round.go
generated
vendored
Normal file
100
vendor/github.com/paulmach/orb/round.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
package orb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// Round will round all the coordinates of the geometry to the given factor.
|
||||
// The default is 6 decimal places.
|
||||
func Round(g Geometry, factor ...int) Geometry {
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
f := float64(DefaultRoundingFactor)
|
||||
if len(factor) > 0 {
|
||||
f = float64(factor[0])
|
||||
}
|
||||
|
||||
switch g := g.(type) {
|
||||
case Point:
|
||||
return Point{
|
||||
math.Round(g[0]*f) / f,
|
||||
math.Round(g[1]*f) / f,
|
||||
}
|
||||
case MultiPoint:
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
roundPoints([]Point(g), f)
|
||||
return g
|
||||
case LineString:
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
roundPoints([]Point(g), f)
|
||||
return g
|
||||
case MultiLineString:
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
for _, ls := range g {
|
||||
roundPoints([]Point(ls), f)
|
||||
}
|
||||
return g
|
||||
case Ring:
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
roundPoints([]Point(g), f)
|
||||
return g
|
||||
case Polygon:
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
for _, r := range g {
|
||||
roundPoints([]Point(r), f)
|
||||
}
|
||||
return g
|
||||
case MultiPolygon:
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
for _, p := range g {
|
||||
for _, r := range p {
|
||||
roundPoints([]Point(r), f)
|
||||
}
|
||||
}
|
||||
return g
|
||||
case Collection:
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := range g {
|
||||
g[i] = Round(g[i], int(f))
|
||||
}
|
||||
return g
|
||||
case Bound:
|
||||
return Bound{
|
||||
Min: Point{
|
||||
math.Round(g.Min[0]*f) / f,
|
||||
math.Round(g.Min[1]*f) / f,
|
||||
},
|
||||
Max: Point{
|
||||
math.Round(g.Max[0]*f) / f,
|
||||
math.Round(g.Max[1]*f) / f,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("geometry type not supported: %T", g))
|
||||
}
|
||||
|
||||
func roundPoints(ps []Point, f float64) {
|
||||
for i := range ps {
|
||||
ps[i][0] = math.Round(ps[i][0]*f) / f
|
||||
ps[i][1] = math.Round(ps[i][1]*f) / f
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user