Skip to content

Instantly share code, notes, and snippets.

@paulofreitas
Last active September 3, 2024 09:34
Show Gist options
  • Save paulofreitas/a6f742b63decf5874c53074865eb6dbf to your computer and use it in GitHub Desktop.
Save paulofreitas/a6f742b63decf5874c53074865eb6dbf to your computer and use it in GitHub Desktop.
<?php
class GeoJSON
{
public function __construct(string $geojson)
{
$this->_struct = json_decode($geojson);
}
public static function fromString(string $geojson)
{
return new static($geojson);
}
public static function fromFile(string $geojson_file)
{
return new static(file_get_contents($geojson_file));
}
public function parse()
{
if ($this->_struct->type == 'Feature') {
return new Feature($this->_struct);
}
if ($this->_struct->type == 'FeatureCollection') {
return new FeatureCollection($this->_struct);
}
throw Exception('Invalid GeoJSON');
}
}
class FeatureCollection
{
public function __construct($struct)
{
$this->_struct = $struct;
$this->validate();
}
public function validate()
{
assert(isset($this->_struct->features), new Exception('Missing feature collection'));
}
public function isCollection()
{
return true;
}
public function features()
{
$features = [];
foreach ($this->_struct->features as $feature) {
$features[] = new Feature($feature);
}
return $features;
}
}
class Feature
{
public function __construct($struct)
{
$this->_struct = $struct;
$this->validate();
}
public function validate()
{
assert(isset($this->_struct->geometry), new Exception('Missing feature geometry'));
assert(isset($this->_struct->properties), new Exception('Missing feature properties'));
}
public function isCollection()
{
return False;
}
public function geometry()
{
return new Geometry($this->_struct->geometry);
}
public function properties()
{
return $this->_struct->properties;
}
}
class Geometry
{
public function __construct($struct)
{
$this->_struct = $struct;
$this->validate();
}
public function validate()
{
assert(isset($this->_struct->type), new Exception('Missing geometry type'));
assert(in_array($this->_struct->type, ['Point', 'Polygon']), new Exception('Unsupported geometry type'));
assert(isset($this->_struct->coordinates), new Exception('Missing geometry coordinates'));
}
public function type()
{
return $this->_struct->type;
}
public function coordinates()
{
return $this->_struct->coordinates;
}
public function parse()
{
if ($this->type() == 'Point') {
list($x, $y) = $this->_struct->coordinates;
return new Point($this->_struct->coordinates);
}
if ($this->type() == 'Polygon') {
return new Polygon($this->_struct->coordinates[0]);
}
throw new Exception('Unsupported geometry type');
}
}
class Point
{
private $_x;
private $_y;
public function __construct(float $x, float $y)
{
$this->setLongitude($x);
$this->setLatitude($y);
}
public function latitude()
{
return $this->_y;
}
public function longitude()
{
return $this->_x;
}
public function setLatitude(float $y)
{
assert($y >= -90 && $y <= 90, new Exception('Invalid latitude degree'));
$this->_y = $y;
}
public function setLongitude(float $x)
{
assert($x >= -180 && $x <= 180, new Exception('Invalid longitude degree'));
$this->_x = $x;
}
}
class Polygon
{
private $_points;
public function __construct(array $points = [])
{
$this->addPoints($points);
}
public function addPoints(array $points)
{
foreach ($points as $point) {
list($x, $y) = $point;
$this->addPoint(new Point($x, $y));
}
}
public function addPoint(Point $point)
{
$this->_points[] = $point;
}
public function contains(Point $point)
{
$contains = false;
$n_points = count($this->_points);
for ($i = 0, $j = $n_points - 1; $i < $n_points; $j = $i++) {
$i_point = $this->_points[$i];
$j_point = $this->_points[$j];
if ((($i_point->longitude() > $point->longitude()) != ($j_point->longitude() > $point->longitude()))
&& ($point->latitude() < ($j_point->latitude() - $i_point->latitude()) * ($point->longitude() - $i_point->longitude()) / ($j_point->longitude() - $i_point->longitude()) + $i_point->latitude())) {
$contains = !$contains;
}
}
return $contains;
}
}
$raw_geojson = '{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-47.498998045921326,
-23.533586090311893
],
[
-47.49819874763489,
-23.532789333203414
],
[
-47.49783396720886,
-23.533109020277173
],
[
-47.49861717224121,
-23.533905775449583
],
[
-47.498998045921326,
-23.533586090311893
]
]
]
},
"properties": {
}
}
]
}';
$feature = GeoJSON::fromString($raw_geojson)->parse();
function polygon_contains($feature, $point)
{
if (!$feature->isCollection()) {
return $feature instanceof Polygon && $geometry->contains($point);
}
foreach ($feature->features() as $_feature) {
$polygon = $_feature->geometry()->parse();
if ($polygon instanceof Polygon && $polygon->contains($point)) {
return True;
}
}
return False;
}
var_dump(polygon_contains($feature, new Point(-47.0869358, -22.9075211))); // false
var_dump(polygon_contains($feature, new Point(-47.49847769737243, -23.533305750397986))); // true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment