Skip to content

Instantly share code, notes, and snippets.

@mcihad
Created January 7, 2024 17:59
Show Gist options
  • Save mcihad/a9cc1b460574199196b05826d1966f40 to your computer and use it in GitHub Desktop.
Save mcihad/a9cc1b460574199196b05826d1966f40 to your computer and use it in GitHub Desktop.
Point in Polygon algorithm and parse coordinates from kml file
package main
import (
"encoding/xml"
"fmt"
"os"
"strconv"
"strings"
)
type Point struct {
X float64 `json:"x"`
Y float64 `json:"y"`
}
type Polygon struct {
Points []Point `json:"points"`
}
type Document struct {
XMLName xml.Name `xml:"kml"`
Document struct {
Locations []struct {
Name string `xml:"name"`
Polygon string `xml:"Polygon>outerBoundaryIs>LinearRing>coordinates"`
} `xml:"Placemark"`
} `xml:"Document"`
}
type Location struct {
Name string `json:"name"`
Polygon Polygon `json:"polygon"`
}
type Locations []Location
func (d *Document) LoadFromXML(filename string) (Locations, error) {
byte, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
err = xml.Unmarshal(byte, d)
if err != nil {
return nil, err
}
locations := Locations{}
for _, location := range d.Document.Locations {
poly := strings.TrimSpace(location.Polygon)
pointArr := strings.Split(poly, " ")
points := []Point{}
for _, point := range pointArr {
xy := strings.Split(point, ",")
x, err := strconv.ParseFloat(xy[0], 64)
if err != nil {
return nil, err
}
y, err := strconv.ParseFloat(xy[1], 64)
if err != nil {
return nil, err
}
points = append(points, Point{X: x, Y: y})
}
locations = append(locations, Location{Name: location.Name, Polygon: Polygon{Points: points}})
}
return locations, nil
}
func Min(x, y float64) float64 {
if x < y {
return x
}
return y
}
func Max(x, y float64) float64 {
if x > y {
return x
}
return y
}
func (p Polygon) ContainsPoint(point Point) bool {
counter := 0
p1 := p.Points[0]
n := len(p.Points)
for i := 1; i <= n; i++ {
p2 := p.Points[i%n]
if point.Y > Min(p1.Y, p2.Y) {
if point.Y <= Max(p1.Y, p2.Y) {
if point.X <= Max(p1.X, p2.X) {
if p1.Y != p2.Y {
xinters := (point.Y-p1.Y)*(p2.X-p1.X)/(p2.Y-p1.Y) + p1.X
if p1.X == p2.X || point.X <= xinters {
counter++
}
}
}
}
}
p1 = p2
}
return counter%2 != 0
}
func main() {
point := Polygon{
Points: []Point{
{X: 36.405282724573055, Y: 39.348697718090555},
{Y: 39.34468045759655, X: 36.385717348689376},
},
}
l := Document{}
locations, err := l.LoadFromXML("locations.kml")
if err != nil {
fmt.Println(err)
}
for _, location := range locations {
if location.Polygon.ContainsPoint(point.Points[0]) {
fmt.Println(location.Name)
}
if location.Polygon.ContainsPoint(point.Points[1]) {
fmt.Println(location.Name)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment