├── composer.json └── src └── Geometry └── Polygon.php /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hannesvdvreken/pip", 3 | "description": "The Polygon class", 4 | "keywords": ["pip","GeoJSON","Polygon","Geometry","Geo","Point-in-polygon"], 5 | "authors": [ 6 | { 7 | "name": "Hannes Van De Vreken", 8 | "email": "vandevreken.hannes@gmail.com" 9 | } 10 | ], 11 | "license" : "MIT", 12 | "require": { 13 | "php": ">=5.6.0" 14 | }, 15 | "require-dev": { 16 | "phpunit/phpunit": "^5.6.3||^8.2.3" 17 | }, 18 | "autoload": { 19 | "psr-4": { 20 | "Geometry\\": "src/Geometry/" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Geometry/Polygon.php: -------------------------------------------------------------------------------- 1 | setOutline($coordinates); 24 | } else { 25 | $this->valid = false; 26 | } 27 | } 28 | 29 | /** 30 | * @param array $coordinates 31 | * 32 | * @return $this 33 | */ 34 | public function setOutline(array $coordinates) 35 | { 36 | $points = []; 37 | $this->valid = true; 38 | 39 | // Check if it has at least 3 points. 40 | if (count($coordinates) >= 3) { 41 | // Check format 42 | foreach ($coordinates as $point) { 43 | if ($this->validPoint($point)) { 44 | $points[] = $point; 45 | } else { 46 | $this->valid = false; 47 | return $this; 48 | } 49 | } 50 | } else { 51 | $this->valid = false; 52 | return $this; 53 | } 54 | 55 | // Make sure the last one is the same as the first one. 56 | if ($this->firstAndLastEqual($points)) { 57 | $points[] = reset($points); 58 | } 59 | 60 | // Assign. 61 | $this->coordinates = $points; 62 | } 63 | 64 | /** 65 | * @return array 66 | */ 67 | public function getOutline() 68 | { 69 | return $this->coordinates; 70 | } 71 | 72 | /** 73 | * @return bool 74 | */ 75 | public function isValid() 76 | { 77 | return $this->valid; 78 | } 79 | 80 | /** 81 | * @param double $latitude 82 | * @param double $longitude 83 | * 84 | * @return bool 85 | */ 86 | public function pip($latitude, $longitude) 87 | { 88 | // init 89 | $left = 0; 90 | $right = 0; 91 | $previous = reset($this->coordinates); 92 | 93 | // calculate 94 | while ($point = next($this->coordinates)) { 95 | $x1 = $previous[0]; 96 | $y1 = $previous[1]; 97 | 98 | $x2 = $point[0]; 99 | $y2 = $point[1]; 100 | 101 | // If the edge is on the same height. 102 | if (max($x2, $x1) >= $latitude && min($x2, $x1) < $latitude) { 103 | // Change edge's direction if needed. 104 | if ($x2 > $x1) { 105 | list($x1, $y1, $x2, $y2) = [$x2, $y2, $x1, $y1]; 106 | } 107 | 108 | // Check position relative to the edge. 109 | $temp = ($x2 - $x1) * ($longitude - $y1) - ($y2 - $y1) * ($latitude - $x1); 110 | $left += ($temp < 0) ? 1 : 0; 111 | } 112 | 113 | // Shift. 114 | $previous = $point; 115 | } 116 | 117 | // Return 118 | return $left % 2 === 1; 119 | } 120 | 121 | /** 122 | * @return bool 123 | */ 124 | public function isClockwise() 125 | { 126 | // Init 127 | $sum = 0; 128 | 129 | // Get the first. 130 | $previous = reset($this->coordinates); 131 | 132 | // Loop. 133 | while ($point = next($this->coordinates)) { 134 | $sum += ($point[0] - $previous[0]) * ($point[1] + $previous[1]); 135 | 136 | // Shift 137 | $previous = $point; 138 | } 139 | 140 | // Reset pointer. 141 | reset($this->coordinates); 142 | 143 | // Return. 144 | return $sum >= 0; 145 | } 146 | 147 | /** 148 | * @return array 149 | */ 150 | public function centroid() 151 | { 152 | // Init 153 | $cx = 0; 154 | $cy = 0; 155 | $a = 0; 156 | 157 | // First point. 158 | $previous = reset($this->coordinates); 159 | 160 | // Loop all points. 161 | while ($point = next($this->coordinates)) { 162 | $temp = ($previous[0] * $point[1]) - ($point[0] * $previous[1]); 163 | $cx += (($previous[0] + $point[0]) * $temp); 164 | $cy += (($previous[1] + $point[1]) * $temp); 165 | $a += $temp * 3; 166 | 167 | // Shift 168 | $previous = $point; 169 | } 170 | 171 | // Reset the pointer. 172 | reset($this->coordinates); 173 | 174 | // Return the centroid. 175 | return [$cx / $a, $cy / $a]; 176 | } 177 | 178 | /** 179 | * @param $point 180 | * 181 | * @return bool 182 | */ 183 | protected function validPoint($point) 184 | { 185 | return is_array($point) && 186 | count($point) == 2 && 187 | is_numeric($point[0]) && 188 | is_numeric($point[1]); 189 | } 190 | 191 | /** 192 | * @param array $points 193 | * 194 | * @return bool 195 | */ 196 | protected function firstAndLastEqual(array $points) 197 | { 198 | // Get first and last. 199 | $first = reset($points); 200 | $last = end($points); 201 | 202 | // Return if the first and the last are the same. 203 | return $first[0] !== $last[0] || $first[1] !== $last[1]; 204 | } 205 | } 206 | --------------------------------------------------------------------------------