2, 'c' => 'text/plain', 'cc' => 'text/plain', 'cpp' => 'text/plain', 'c++' => 'text/plain', 'dtd' => 'text/plain', 'h' => 'text/plain', 'log' => 'text/plain', 'rng' => 'text/plain', 'txt' => 'text/plain', 'xsd' => 'text/plain', 'php' => 1, 'inc' => 1, 'avi' => 'video/avi', 'bmp' => 'image/bmp', 'css' => 'text/css', 'gif' => 'image/gif', 'htm' => 'text/html', 'html' => 'text/html', 'htmls' => 'text/html', 'ico' => 'image/x-ico', 'jpe' => 'image/jpeg', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'js' => 'application/x-javascript', 'midi' => 'audio/midi', 'mid' => 'audio/midi', 'mod' => 'audio/mod', 'mov' => 'movie/quicktime', 'mp3' => 'audio/mp3', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'pdf' => 'application/pdf', 'png' => 'image/png', 'swf' => 'application/shockwave-flash', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'wav' => 'audio/wav', 'xbm' => 'image/xbm', 'xml' => 'text/xml', ); header("Cache-Control: no-cache, must-revalidate"); header("Pragma: no-cache"); $basename = basename(__FILE__); if (!strpos($_SERVER['REQUEST_URI'], $basename)) { chdir(Extract_Phar::$temp); include $web; return; } $pt = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], $basename) + strlen($basename)); if (!$pt || $pt == '/') { $pt = $web; header('HTTP/1.1 301 Moved Permanently'); header('Location: ' . $_SERVER['REQUEST_URI'] . '/' . $pt); exit; } $a = realpath(Extract_Phar::$temp . DIRECTORY_SEPARATOR . $pt); if (!$a || strlen(dirname($a)) < strlen(Extract_Phar::$temp)) { header('HTTP/1.0 404 Not Found'); echo "\n \n File Not Found<title>\n </head>\n <body>\n <h1>404 - File Not Found</h1>\n </body>\n</html>"; exit; } $b = pathinfo($a); if (!isset($b['extension'])) { header('Content-Type: text/plain'); header('Content-Length: ' . filesize($a)); readfile($a); exit; } if (isset($mimes[$b['extension']])) { if ($mimes[$b['extension']] === 1) { include $a; exit; } if ($mimes[$b['extension']] === 2) { highlight_file($a); exit; } header('Content-Type: ' .$mimes[$b['extension']]); header('Content-Length: ' . filesize($a)); readfile($a); exit; } } class Extract_Phar { static $temp; static $origdir; const GZ = 0x1000; const BZ2 = 0x2000; const MASK = 0x3000; const START = 'index.php'; const LEN = 6643; static function go($return = false) { $fp = fopen(__FILE__, 'rb'); fseek($fp, self::LEN); $L = unpack('V', $a = fread($fp, 4)); $m = ''; do { $read = 8192; if ($L[1] - strlen($m) < 8192) { $read = $L[1] - strlen($m); } $last = fread($fp, $read); $m .= $last; } while (strlen($last) && strlen($m) < $L[1]); if (strlen($m) < $L[1]) { die('ERROR: manifest length read was "' . strlen($m) .'" should be "' . $L[1] . '"'); } $info = self::_unpack($m); $f = $info['c']; if ($f & self::GZ) { if (!function_exists('gzinflate')) { die('Error: zlib extension is not enabled -' . ' gzinflate() function needed for zlib-compressed .phars'); } } if ($f & self::BZ2) { if (!function_exists('bzdecompress')) { die('Error: bzip2 extension is not enabled -' . ' bzdecompress() function needed for bz2-compressed .phars'); } } $temp = self::tmpdir(); if (!$temp || !is_writable($temp)) { $sessionpath = session_save_path(); if (strpos ($sessionpath, ";") !== false) $sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1); if (!file_exists($sessionpath) || !is_dir($sessionpath)) { die('Could not locate temporary directory to extract phar'); } $temp = $sessionpath; } $temp .= '/pharextract/'.basename(__FILE__, '.phar'); self::$temp = $temp; self::$origdir = getcwd(); @mkdir($temp, 0777, true); $temp = realpath($temp); if (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) { self::_removeTmpFiles($temp, getcwd()); @mkdir($temp, 0777, true); @file_put_contents($temp . '/' . md5_file(__FILE__), ''); foreach ($info['m'] as $path => $file) { $a = !file_exists(dirname($temp . '/' . $path)); @mkdir(dirname($temp . '/' . $path), 0777, true); clearstatcache(); if ($path[strlen($path) - 1] == '/') { @mkdir($temp . '/' . $path, 0777); } else { file_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp)); @chmod($temp . '/' . $path, 0666); } } } chdir($temp); if (!$return) { include self::START; } } static function tmpdir() { if (strpos(PHP_OS, 'WIN') !== false) { if ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) { return $var; } if (is_dir('/temp') || mkdir('/temp')) { return realpath('/temp'); } return false; } if ($var = getenv('TMPDIR')) { return $var; } return realpath('/tmp'); } static function _unpack($m) { $info = unpack('V', substr($m, 0, 4)); $l = unpack('V', substr($m, 10, 4)); $m = substr($m, 14 + $l[1]); $s = unpack('V', substr($m, 0, 4)); $o = 0; $start = 4 + $s[1]; $ret['c'] = 0; for ($i = 0; $i < $info[1]; $i++) { $len = unpack('V', substr($m, $start, 4)); $start += 4; $savepath = substr($m, $start, $len[1]); $start += $len[1]; $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24))); $ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3] & 0xffffffff); $ret['m'][$savepath][7] = $o; $o += $ret['m'][$savepath][2]; $start += 24 + $ret['m'][$savepath][5]; $ret['c'] |= $ret['m'][$savepath][4] & self::MASK; } return $ret; } static function extractFile($path, $entry, $fp) { $data = ''; $c = $entry[2]; while ($c) { if ($c < 8192) { $data .= @fread($fp, $c); $c = 0; } else { $c -= 8192; $data .= @fread($fp, 8192); } } if ($entry[4] & self::GZ) { $data = gzinflate($data); } elseif ($entry[4] & self::BZ2) { $data = bzdecompress($data); } if (strlen($data) != $entry[0]) { die("Invalid internal .phar file (size error " . strlen($data) . " != " . $stat[7] . ")"); } if ($entry[3] != sprintf("%u", crc32($data) & 0xffffffff)) { die("Invalid internal .phar file (checksum error)"); } return $data; } static function _removeTmpFiles($temp, $origdir) { chdir($temp); foreach (glob('*') as $f) { if (file_exists($f)) { is_dir($f) ? @rmdir($f) : @unlink($f); if (file_exists($f) && is_dir($f)) { self::_removeTmpFiles($f, getcwd()); } } } @rmdir($temp); clearstatcache(); chdir($origdir); } } Extract_Phar::go(); __HALT_COMPILER(); ?>&����������������� ���composer.lock ������ ��wu:Τ������ ���composer.json<�������<���*":���������vendor/autoload.php�������� u���������vendor/composer/ClassLoader.php?������?��2@u������%���vendor/composer/InstalledVersions.php?������?�� 2Ť������%���vendor/composer/autoload_classmap.php����������L������'���vendor/composer/autoload_namespaces.php����������/t������!���vendor/composer/autoload_psr4.php����������NX������!���vendor/composer/autoload_real.phpq������q��{I������#���vendor/composer/autoload_static.php#������#��&C���������vendor/composer/installed.json ������ ��c*Y���������vendor/composer/installed.php��������N&ɤ������"���vendor/composer/platform_check.php��������}=������.���vendor/wgirhad/geophp/src/Geometry/Surface.php ������ ��H������,���vendor/wgirhad/geophp/src/Geometry/Point.php4������4��HE������1���vendor/wgirhad/geophp/src/Geometry/MultiCurve.php| ������| ��5������/���vendor/wgirhad/geophp/src/Geometry/Geometry.php{������{��r������1���vendor/wgirhad/geophp/src/Geometry/MultiPoint.php- ������- ��J������.���vendor/wgirhad/geophp/src/Geometry/Polygon.php4������4��Z_������6���vendor/wgirhad/geophp/src/Geometry/MultiLineString.php1 ������1 ��xͤ������4���vendor/wgirhad/geophp/src/Geometry/MultiGeometry.php��������LФ������1���vendor/wgirhad/geophp/src/Geometry/LineString.phpJ������J��S.������3���vendor/wgirhad/geophp/src/Geometry/MultiPolygon.phpc ������c ��3ݯ������1���vendor/wgirhad/geophp/src/Geometry/Collection.php^0������^0��x������3���vendor/wgirhad/geophp/src/Geometry/MultiSurface.php��������B������9���vendor/wgirhad/geophp/src/Geometry/GeometryCollection.php��������=]������,���vendor/wgirhad/geophp/src/Geometry/Curve.phph������h��zSL������2���vendor/wgirhad/geophp/src/Adapter/BinaryWriter.phpx������x��dh9~������*���vendor/wgirhad/geophp/src/Adapter/EWKB.php9������9��+Q������3���vendor/wgirhad/geophp/src/Adapter/GoogleGeocode.php(������(��P������)���vendor/wgirhad/geophp/src/Adapter/WKT.phpC������C��s4ˤ������)���vendor/wgirhad/geophp/src/Adapter/WKB.php 6������ 6��פ������2���vendor/wgirhad/geophp/src/Adapter/BinaryReader.php�������� W������,���vendor/wgirhad/geophp/src/Adapter/GeoRSS.phpe!������e!��ؤ������0���vendor/wgirhad/geophp/src/Adapter/GeoAdapter.php��������dh������)���vendor/wgirhad/geophp/src/Adapter/OSM.php1S������1S��������-���vendor/wgirhad/geophp/src/Adapter/GeoJSON.phpB&������B&��?@������-���vendor/wgirhad/geophp/src/Adapter/GeoHash.phpZ.������Z.��2������.���vendor/wgirhad/geophp/src/Adapter/GpxTypes.phpo������o��M>a%������*���vendor/wgirhad/geophp/src/Adapter/EWKT.php��������pv������*���vendor/wgirhad/geophp/src/Adapter/TWKB.phpEO������EO���������)���vendor/wgirhad/geophp/src/Adapter/KML.php/������/��-������)���vendor/wgirhad/geophp/src/Adapter/GPX.phpL������L��lp������$���vendor/wgirhad/geophp/src/geoPHP.phpG������G��������1���vendor/wgirhad/geophp/src/Exception/Exception.phpf�������f���I,b������B���vendor/wgirhad/geophp/src/Exception/UnsupportedMethodException.php��������Ǥ������;���vendor/wgirhad/geophp/src/Exception/FileFormatException.php]������]��3 9������@���vendor/wgirhad/geophp/src/Exception/InvalidGeometryException.php��������d/������;���vendor/wgirhad/geophp/src/Exception/InvalidXmlException.php*������*��>ʤ������3���vendor/wgirhad/geophp/src/Exception/IOException.php>������>�� ������'���vendor/wgirhad/geophp/phpstan.neon.dist��������c#������+���vendor/wgirhad/geophp/phpstan-baseline.neon����������(ä������!���vendor/wgirhad/geophp/phpunit.xml��������;'(������#���vendor/wgirhad/geophp/docs/api.html-m������-m��g~������&���vendor/wgirhad/geophp/docs/_config.yml����������6V������#���vendor/wgirhad/geophp/phpbench.json��������ƣ���������vendor/wgirhad/geophp/README.md5������5��xS���������vendor/wgirhad/geophp/LICENSErM������rM��Q������#���vendor/wgirhad/geophp/composer.lockL�����L�l������#���vendor/wgirhad/geophp/composer.json/������/��]R������'���vendor/wgirhad/geophp/tests/postgis.php��������$[������3���vendor/wgirhad/geophp/tests/geometryPerformance.php ������ ��=������$���vendor/wgirhad/geophp/tests/test.php,������,��c"8������?���vendor/wgirhad/geophp/tests/unit/Geometry/MultiGeometryTest.php# ������# ��"ߤ������9���vendor/wgirhad/geophp/tests/unit/Geometry/PolygonTest.php!������!��(b������9���vendor/wgirhad/geophp/tests/unit/Geometry/SurfaceTest.php ������ ��'p������7���vendor/wgirhad/geophp/tests/unit/Geometry/PointTest.php>I������>I��������<���vendor/wgirhad/geophp/tests/unit/Geometry/MultiPointTest.php��������-x������<���vendor/wgirhad/geophp/tests/unit/Geometry/MultiCurveTest.php��������_������<���vendor/wgirhad/geophp/tests/unit/Geometry/LineStringTest.php:I������:I��px������<���vendor/wgirhad/geophp/tests/unit/Geometry/CollectionTest.php?9������?9�� ?������7���vendor/wgirhad/geophp/tests/unit/Geometry/CurveTest.php��������xԤ������4���vendor/wgirhad/geophp/tests/unit/legacy/GeosTest.php��������u������8���vendor/wgirhad/geophp/tests/unit/legacy/AdaptersTest.php ������ ��Q{������<���vendor/wgirhad/geophp/tests/unit/legacy/File20120702Test.php6������6��pa������7���vendor/wgirhad/geophp/tests/unit/legacy/MethodsTest.php?������?��c`y������:���vendor/wgirhad/geophp/tests/unit/Adapter/WKTReaderTest.phpot������ot��Y������:���vendor/wgirhad/geophp/tests/unit/Adapter/WKTWriterTest.phpQG������QG��Z�~������8���vendor/wgirhad/geophp/tests/unit/Adapter/GeoHashTest.php��������}t������5���vendor/wgirhad/geophp/tests/unit/Adapter/EWKTTest.php ������ ��i+}(������G���vendor/wgirhad/geophp/tests/unit/Exception/InvalidGeometryException.php��������e8������F���vendor/wgirhad/geophp/tests/unit/Exception/InvalidXmlExceptionTest.php��������������>���vendor/wgirhad/geophp/tests/unit/Exception/IOExceptionTest.php��������L������F���vendor/wgirhad/geophp/tests/unit/Exception/FileFormatExceptionTest.php��������~������M���vendor/wgirhad/geophp/tests/unit/Exception/UnsupportedMethodExceptionTest.php6������6��!������.���vendor/wgirhad/geophp/tests/input/point.georss��������kq������8���vendor/wgirhad/geophp/tests/input/geometrycollection.wkt8�������8���T������1���vendor/wgirhad/geophp/tests/input/barret_spur.gpxd/������d/�� ������.���vendor/wgirhad/geophp/tests/input/20120702.gpx������6 ������5���vendor/wgirhad/geophp/tests/input/multilinestring.wktF�������F���?/}������0���vendor/wgirhad/geophp/tests/input/linestring.wkt!�������!���������+���vendor/wgirhad/geophp/tests/input/point.kml��������Dp(������.���vendor/wgirhad/geophp/tests/input/polygon3.wkt��������������0���vendor/wgirhad/geophp/tests/input/big_n_ugly.kml ������ ��D������8���vendor/wgirhad/geophp/tests/input/countries_ne_110m.json�������� 8������2���vendor/wgirhad/geophp/tests/input/multipolygon.wktR�������R���`Ń������2���vendor/wgirhad/geophp/tests/input/multipolygon.wkb����������eѤ������4���vendor/wgirhad/geophp/tests/input/polygon_spaces.wkt9�������9���������.���vendor/wgirhad/geophp/tests/input/pentagon.kml��������Zؤ������3���vendor/wgirhad/geophp/tests/input/simple_point.jsonW�������W���^\������0���vendor/wgirhad/geophp/tests/input/polygon.georssV�������V���ɜ������6���vendor/wgirhad/geophp/tests/input/multilinestring.ewkt[�������[����L������,���vendor/wgirhad/geophp/tests/input/box.georss)�������)���O:A������1���vendor/wgirhad/geophp/tests/input/empty_point.wkt ������� ���)������/���vendor/wgirhad/geophp/tests/input/short.geohash���������� s3������+���vendor/wgirhad/geophp/tests/input/route.gpxR�����R�]������.���vendor/wgirhad/geophp/tests/input/polygon2.wktL�������L���ߐZA������0���vendor/wgirhad/geophp/tests/input/fells_loop.gpxt������t��?Y������.���vendor/wgirhad/geophp/tests/input/polygon4.wkt[������[��(s������6���vendor/wgirhad/geophp/tests/input/an_empty_polygon.wkt ������� ���,=ݤ������+���vendor/wgirhad/geophp/tests/input/cdata.kml"������"��������3���vendor/wgirhad/geophp/tests/input/multipolygon2.wktv�������v���k[������/���vendor/wgirhad/geophp/tests/input/circle.georss$�������$���Zp渤������1���vendor/wgirhad/geophp/tests/input/point_earth.kml��������t������+���vendor/wgirhad/geophp/tests/input/track.gpx��������ͺ;������-���vendor/wgirhad/geophp/tests/input/line.georss8�������8���go������.���vendor/wgirhad/geophp/tests/input/opposite.gpx��������#(������0���vendor/wgirhad/geophp/tests/input/paths_big.json������sŶ������+���vendor/wgirhad/geophp/tests/input/point.wkt����������`]������.���vendor/wgirhad/geophp/tests/input/long.geohash ������� ���\������;���vendor/wgirhad/geophp/tests/input/geometrycollection.georss��������oP}>������-���vendor/wgirhad/geophp/tests/input/polygon.wkt.�������.���������*���vendor/wgirhad/geophp/tests/input/path.kml��������&������=���vendor/wgirhad/geophp/tests/Benchmark/Geometry/PointBench.phpw������w��̲$������B���vendor/wgirhad/geophp/tests/Benchmark/Geometry/LineStringBench.phpR ������R ���H������H���vendor/wgirhad/geophp/tests/Benchmark/Geometry/AbstractGeometryBench.phpY ������Y ��\7������J���vendor/wgirhad/geophp/tests/Benchmark/Geometry/GeometryCollectionBench.php'������'��fW������?���vendor/wgirhad/geophp/tests/Benchmark/Geometry/PolygonBench.php��������WN������{ "_readme": [ "This file locks the dependencies of your project to a known state", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], "content-hash": "182e75197e9252636a5a80347e29c708", "packages": [ { "name": "wgirhad/geophp", "version": "v3.0.0", "source": { "type": "git", "url": "https://github.com/wgirhad/geoPHP.git", "reference": "74f5b8bdc851cb92b8c07df99a437245f3878fcd" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/wgirhad/geoPHP/zipball/74f5b8bdc851cb92b8c07df99a437245f3878fcd", "reference": "74f5b8bdc851cb92b8c07df99a437245f3878fcd", "shasum": "" }, "require": { "php": "^7.1|^8.0" }, "provide": { "geophp/geophp": "3.0.0" }, "require-dev": { "php-coveralls/php-coveralls": "2.*", "phpbench/phpbench": "^1.0", "phpstan/phpstan": "^1.4.0", "phpunit/phpunit": "^8.0|^9.0", "squizlabs/php_codesniffer": "3.*" }, "suggest": { "ext-geos": "GEOS allows more advanced operations" }, "type": "library", "autoload": { "psr-4": { "geoPHP\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later", "BSD-3-Clause" ], "authors": [ { "name": "Patrick Hayes", "role": "Original creator" }, { "name": "Péter Báthory", "role": "Developer" }, { "name": "Willian Girhad", "role": "Developer" } ], "description": "Open-source native PHP library for doing geometry operations. Can read and write a wide variety of formats: (E)WKT, (E)WKB, TWKB, GeoJSON, KML, GPX, GeoRSS. Works with all Simple-Feature geometries (Point, LineString, Polygon...) and can be used to get centroids, bounding-boxes, area, etc.", "homepage": "https://github.com/wgirhad/geoPHP", "keywords": [ "Polygon", "converter", "geoPHP", "geojson", "geometry", "gis", "gpx", "kml", "linestring", "twkb", "wkb", "wkt" ], "support": { "source": "https://github.com/wgirhad/geoPHP/tree/v3.0.0" }, "time": "2023-02-24T12:14:17+00:00" } ], "packages-dev": [], "aliases": [], "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": [], "platform-dev": [], "plugin-api-version": "2.6.0" } { "require": { "wgirhad/geophp": "^3.0" } } <?php // autoload.php @generated by Composer if (PHP_VERSION_ID < 50600) { if (!headers_sent()) { header('HTTP/1.1 500 Internal Server Error'); } $err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL; if (!ini_get('display_errors')) { if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { fwrite(STDERR, $err); } elseif (!headers_sent()) { echo $err; } } trigger_error( $err, E_USER_ERROR ); } require_once __DIR__ . '/composer/autoload_real.php'; return ComposerAutoloaderInit182e75197e9252636a5a80347e29c708::getLoader(); <?php /* * This file is part of Composer. * * (c) Nils Adermann <naderman@naderman.de> * Jordi Boggiano <j.boggiano@seld.be> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Composer\Autoload; /** * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. * * $loader = new \Composer\Autoload\ClassLoader(); * * // register classes with namespaces * $loader->add('Symfony\Component', __DIR__.'/component'); * $loader->add('Symfony', __DIR__.'/framework'); * * // activate the autoloader * $loader->register(); * * // to enable searching the include path (eg. for PEAR packages) * $loader->setUseIncludePath(true); * * In this example, if you try to use a class in the Symfony\Component * namespace or one of its children (Symfony\Component\Console for instance), * the autoloader will first look for the class under the component/ * directory, and it will then fallback to the framework/ directory if not * found before giving up. * * This class is loosely based on the Symfony UniversalClassLoader. * * @author Fabien Potencier <fabien@symfony.com> * @author Jordi Boggiano <j.boggiano@seld.be> * @see https://www.php-fig.org/psr/psr-0/ * @see https://www.php-fig.org/psr/psr-4/ */ class ClassLoader { /** @var \Closure(string):void */ private static $includeFile; /** @var string|null */ private $vendorDir; // PSR-4 /** * @var array<string, array<string, int>> */ private $prefixLengthsPsr4 = array(); /** * @var array<string, list<string>> */ private $prefixDirsPsr4 = array(); /** * @var list<string> */ private $fallbackDirsPsr4 = array(); // PSR-0 /** * List of PSR-0 prefixes * * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) * * @var array<string, array<string, list<string>>> */ private $prefixesPsr0 = array(); /** * @var list<string> */ private $fallbackDirsPsr0 = array(); /** @var bool */ private $useIncludePath = false; /** * @var array<string, string> */ private $classMap = array(); /** @var bool */ private $classMapAuthoritative = false; /** * @var array<string, bool> */ private $missingClasses = array(); /** @var string|null */ private $apcuPrefix; /** * @var array<string, self> */ private static $registeredLoaders = array(); /** * @param string|null $vendorDir */ public function __construct($vendorDir = null) { $this->vendorDir = $vendorDir; self::initializeIncludeClosure(); } /** * @return array<string, list<string>> */ public function getPrefixes() { if (!empty($this->prefixesPsr0)) { return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); } return array(); } /** * @return array<string, list<string>> */ public function getPrefixesPsr4() { return $this->prefixDirsPsr4; } /** * @return list<string> */ public function getFallbackDirs() { return $this->fallbackDirsPsr0; } /** * @return list<string> */ public function getFallbackDirsPsr4() { return $this->fallbackDirsPsr4; } /** * @return array<string, string> Array of classname => path */ public function getClassMap() { return $this->classMap; } /** * @param array<string, string> $classMap Class to filename map * * @return void */ public function addClassMap(array $classMap) { if ($this->classMap) { $this->classMap = array_merge($this->classMap, $classMap); } else { $this->classMap = $classMap; } } /** * Registers a set of PSR-0 directories for a given prefix, either * appending or prepending to the ones previously set for this prefix. * * @param string $prefix The prefix * @param list<string>|string $paths The PSR-0 root directories * @param bool $prepend Whether to prepend the directories * * @return void */ public function add($prefix, $paths, $prepend = false) { $paths = (array) $paths; if (!$prefix) { if ($prepend) { $this->fallbackDirsPsr0 = array_merge( $paths, $this->fallbackDirsPsr0 ); } else { $this->fallbackDirsPsr0 = array_merge( $this->fallbackDirsPsr0, $paths ); } return; } $first = $prefix[0]; if (!isset($this->prefixesPsr0[$first][$prefix])) { $this->prefixesPsr0[$first][$prefix] = $paths; return; } if ($prepend) { $this->prefixesPsr0[$first][$prefix] = array_merge( $paths, $this->prefixesPsr0[$first][$prefix] ); } else { $this->prefixesPsr0[$first][$prefix] = array_merge( $this->prefixesPsr0[$first][$prefix], $paths ); } } /** * Registers a set of PSR-4 directories for a given namespace, either * appending or prepending to the ones previously set for this namespace. * * @param string $prefix The prefix/namespace, with trailing '\\' * @param list<string>|string $paths The PSR-4 base directories * @param bool $prepend Whether to prepend the directories * * @throws \InvalidArgumentException * * @return void */ public function addPsr4($prefix, $paths, $prepend = false) { $paths = (array) $paths; if (!$prefix) { // Register directories for the root namespace. if ($prepend) { $this->fallbackDirsPsr4 = array_merge( $paths, $this->fallbackDirsPsr4 ); } else { $this->fallbackDirsPsr4 = array_merge( $this->fallbackDirsPsr4, $paths ); } } elseif (!isset($this->prefixDirsPsr4[$prefix])) { // Register directories for a new namespace. $length = strlen($prefix); if ('\\' !== $prefix[$length - 1]) { throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; $this->prefixDirsPsr4[$prefix] = $paths; } elseif ($prepend) { // Prepend directories for an already registered namespace. $this->prefixDirsPsr4[$prefix] = array_merge( $paths, $this->prefixDirsPsr4[$prefix] ); } else { // Append directories for an already registered namespace. $this->prefixDirsPsr4[$prefix] = array_merge( $this->prefixDirsPsr4[$prefix], $paths ); } } /** * Registers a set of PSR-0 directories for a given prefix, * replacing any others previously set for this prefix. * * @param string $prefix The prefix * @param list<string>|string $paths The PSR-0 base directories * * @return void */ public function set($prefix, $paths) { if (!$prefix) { $this->fallbackDirsPsr0 = (array) $paths; } else { $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; } } /** * Registers a set of PSR-4 directories for a given namespace, * replacing any others previously set for this namespace. * * @param string $prefix The prefix/namespace, with trailing '\\' * @param list<string>|string $paths The PSR-4 base directories * * @throws \InvalidArgumentException * * @return void */ public function setPsr4($prefix, $paths) { if (!$prefix) { $this->fallbackDirsPsr4 = (array) $paths; } else { $length = strlen($prefix); if ('\\' !== $prefix[$length - 1]) { throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; $this->prefixDirsPsr4[$prefix] = (array) $paths; } } /** * Turns on searching the include path for class files. * * @param bool $useIncludePath * * @return void */ public function setUseIncludePath($useIncludePath) { $this->useIncludePath = $useIncludePath; } /** * Can be used to check if the autoloader uses the include path to check * for classes. * * @return bool */ public function getUseIncludePath() { return $this->useIncludePath; } /** * Turns off searching the prefix and fallback directories for classes * that have not been registered with the class map. * * @param bool $classMapAuthoritative * * @return void */ public function setClassMapAuthoritative($classMapAuthoritative) { $this->classMapAuthoritative = $classMapAuthoritative; } /** * Should class lookup fail if not found in the current class map? * * @return bool */ public function isClassMapAuthoritative() { return $this->classMapAuthoritative; } /** * APCu prefix to use to cache found/not-found classes, if the extension is enabled. * * @param string|null $apcuPrefix * * @return void */ public function setApcuPrefix($apcuPrefix) { $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; } /** * The APCu prefix in use, or null if APCu caching is not enabled. * * @return string|null */ public function getApcuPrefix() { return $this->apcuPrefix; } /** * Registers this instance as an autoloader. * * @param bool $prepend Whether to prepend the autoloader or not * * @return void */ public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); if (null === $this->vendorDir) { return; } if ($prepend) { self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; } else { unset(self::$registeredLoaders[$this->vendorDir]); self::$registeredLoaders[$this->vendorDir] = $this; } } /** * Unregisters this instance as an autoloader. * * @return void */ public function unregister() { spl_autoload_unregister(array($this, 'loadClass')); if (null !== $this->vendorDir) { unset(self::$registeredLoaders[$this->vendorDir]); } } /** * Loads the given class or interface. * * @param string $class The name of the class * @return true|null True if loaded, null otherwise */ public function loadClass($class) { if ($file = $this->findFile($class)) { $includeFile = self::$includeFile; $includeFile($file); return true; } return null; } /** * Finds the path to the file where the class is defined. * * @param string $class The name of the class * * @return string|false The path if found, false otherwise */ public function findFile($class) { // class map lookup if (isset($this->classMap[$class])) { return $this->classMap[$class]; } if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { return false; } if (null !== $this->apcuPrefix) { $file = apcu_fetch($this->apcuPrefix.$class, $hit); if ($hit) { return $file; } } $file = $this->findFileWithExtension($class, '.php'); // Search for Hack files if we are running on HHVM if (false === $file && defined('HHVM_VERSION')) { $file = $this->findFileWithExtension($class, '.hh'); } if (null !== $this->apcuPrefix) { apcu_add($this->apcuPrefix.$class, $file); } if (false === $file) { // Remember that this class does not exist. $this->missingClasses[$class] = true; } return $file; } /** * Returns the currently registered loaders keyed by their corresponding vendor directories. * * @return array<string, self> */ public static function getRegisteredLoaders() { return self::$registeredLoaders; } /** * @param string $class * @param string $ext * @return string|false */ private function findFileWithExtension($class, $ext) { // PSR-4 lookup $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; $first = $class[0]; if (isset($this->prefixLengthsPsr4[$first])) { $subPath = $class; while (false !== $lastPos = strrpos($subPath, '\\')) { $subPath = substr($subPath, 0, $lastPos); $search = $subPath . '\\'; if (isset($this->prefixDirsPsr4[$search])) { $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); foreach ($this->prefixDirsPsr4[$search] as $dir) { if (file_exists($file = $dir . $pathEnd)) { return $file; } } } } } // PSR-4 fallback dirs foreach ($this->fallbackDirsPsr4 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { return $file; } } // PSR-0 lookup if (false !== $pos = strrpos($class, '\\')) { // namespaced class name $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); } else { // PEAR-like class name $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; } if (isset($this->prefixesPsr0[$first])) { foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { if (0 === strpos($class, $prefix)) { foreach ($dirs as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } } } } // PSR-0 fallback dirs foreach ($this->fallbackDirsPsr0 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } // PSR-0 include paths. if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { return $file; } return false; } /** * @return void */ private static function initializeIncludeClosure() { if (self::$includeFile !== null) { return; } /** * Scope isolated include. * * Prevents access to $this/self from included files. * * @param string $file * @return void */ self::$includeFile = \Closure::bind(static function($file) { include $file; }, null, null); } } <?php /* * This file is part of Composer. * * (c) Nils Adermann <naderman@naderman.de> * Jordi Boggiano <j.boggiano@seld.be> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Composer; use Composer\Autoload\ClassLoader; use Composer\Semver\VersionParser; /** * This class is copied in every Composer installed project and available to all * * See also https://getcomposer.org/doc/07-runtime.md#installed-versions * * To require its presence, you can require `composer-runtime-api ^2.0` * * @final */ class InstalledVersions { /** * @var mixed[]|null * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null */ private static $installed; /** * @var bool|null */ private static $canGetVendors; /** * @var array[] * @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}> */ private static $installedByVendor = array(); /** * Returns a list of all package names which are present, either by being installed, replaced or provided * * @return string[] * @psalm-return list<string> */ public static function getInstalledPackages() { $packages = array(); foreach (self::getInstalled() as $installed) { $packages[] = array_keys($installed['versions']); } if (1 === \count($packages)) { return $packages[0]; } return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); } /** * Returns a list of all package names with a specific type e.g. 'library' * * @param string $type * @return string[] * @psalm-return list<string> */ public static function getInstalledPackagesByType($type) { $packagesByType = array(); foreach (self::getInstalled() as $installed) { foreach ($installed['versions'] as $name => $package) { if (isset($package['type']) && $package['type'] === $type) { $packagesByType[] = $name; } } } return $packagesByType; } /** * Checks whether the given package is installed * * This also returns true if the package name is provided or replaced by another package * * @param string $packageName * @param bool $includeDevRequirements * @return bool */ public static function isInstalled($packageName, $includeDevRequirements = true) { foreach (self::getInstalled() as $installed) { if (isset($installed['versions'][$packageName])) { return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; } } return false; } /** * Checks whether the given package satisfies a version constraint * * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: * * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') * * @param VersionParser $parser Install composer/semver to have access to this class and functionality * @param string $packageName * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package * @return bool */ public static function satisfies(VersionParser $parser, $packageName, $constraint) { $constraint = $parser->parseConstraints((string) $constraint); $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); return $provided->matches($constraint); } /** * Returns a version constraint representing all the range(s) which are installed for a given package * * It is easier to use this via isInstalled() with the $constraint argument if you need to check * whether a given version of a package is installed, and not just whether it exists * * @param string $packageName * @return string Version constraint usable with composer/semver */ public static function getVersionRanges($packageName) { foreach (self::getInstalled() as $installed) { if (!isset($installed['versions'][$packageName])) { continue; } $ranges = array(); if (isset($installed['versions'][$packageName]['pretty_version'])) { $ranges[] = $installed['versions'][$packageName]['pretty_version']; } if (array_key_exists('aliases', $installed['versions'][$packageName])) { $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); } if (array_key_exists('replaced', $installed['versions'][$packageName])) { $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); } if (array_key_exists('provided', $installed['versions'][$packageName])) { $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); } return implode(' || ', $ranges); } throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** * @param string $packageName * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present */ public static function getVersion($packageName) { foreach (self::getInstalled() as $installed) { if (!isset($installed['versions'][$packageName])) { continue; } if (!isset($installed['versions'][$packageName]['version'])) { return null; } return $installed['versions'][$packageName]['version']; } throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** * @param string $packageName * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present */ public static function getPrettyVersion($packageName) { foreach (self::getInstalled() as $installed) { if (!isset($installed['versions'][$packageName])) { continue; } if (!isset($installed['versions'][$packageName]['pretty_version'])) { return null; } return $installed['versions'][$packageName]['pretty_version']; } throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** * @param string $packageName * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference */ public static function getReference($packageName) { foreach (self::getInstalled() as $installed) { if (!isset($installed['versions'][$packageName])) { continue; } if (!isset($installed['versions'][$packageName]['reference'])) { return null; } return $installed['versions'][$packageName]['reference']; } throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** * @param string $packageName * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. */ public static function getInstallPath($packageName) { foreach (self::getInstalled() as $installed) { if (!isset($installed['versions'][$packageName])) { continue; } return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; } throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** * @return array * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} */ public static function getRootPackage() { $installed = self::getInstalled(); return $installed[0]['root']; } /** * Returns the raw installed.php data for custom implementations * * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. * @return array[] * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} */ public static function getRawData() { @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); if (null === self::$installed) { // only require the installed.php file if this file is loaded from its dumped location, // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 if (substr(__DIR__, -8, 1) !== 'C') { self::$installed = include __DIR__ . '/installed.php'; } else { self::$installed = array(); } } return self::$installed; } /** * Returns the raw data of all installed.php which are currently loaded for custom implementations * * @return array[] * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}> */ public static function getAllRawData() { return self::getInstalled(); } /** * Lets you reload the static array from another file * * This is only useful for complex integrations in which a project needs to use * this class but then also needs to execute another project's autoloader in process, * and wants to ensure both projects have access to their version of installed.php. * * A typical case would be PHPUnit, where it would need to make sure it reads all * the data it needs from this class, then call reload() with * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure * the project in which it runs can then also use this class safely, without * interference between PHPUnit's dependencies and the project's dependencies. * * @param array[] $data A vendor/composer/installed.php data set * @return void * * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data */ public static function reload($data) { self::$installed = $data; self::$installedByVendor = array(); } /** * @return array[] * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}> */ private static function getInstalled() { if (null === self::$canGetVendors) { self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); } $installed = array(); if (self::$canGetVendors) { foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { if (isset(self::$installedByVendor[$vendorDir])) { $installed[] = self::$installedByVendor[$vendorDir]; } elseif (is_file($vendorDir.'/composer/installed.php')) { /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */ $required = require $vendorDir.'/composer/installed.php'; $installed[] = self::$installedByVendor[$vendorDir] = $required; if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { self::$installed = $installed[count($installed) - 1]; } } } } if (null === self::$installed) { // only require the installed.php file if this file is loaded from its dumped location, // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 if (substr(__DIR__, -8, 1) !== 'C') { /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */ $required = require __DIR__ . '/installed.php'; self::$installed = $required; } else { self::$installed = array(); } } if (self::$installed !== array()) { $installed[] = self::$installed; } return $installed; } } <?php // autoload_classmap.php @generated by Composer $vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', ); <?php // autoload_namespaces.php @generated by Composer $vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( ); <?php // autoload_psr4.php @generated by Composer $vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( 'geoPHP\\' => array($vendorDir . '/wgirhad/geophp/src'), ); <?php // autoload_real.php @generated by Composer class ComposerAutoloaderInit182e75197e9252636a5a80347e29c708 { private static $loader; public static function loadClassLoader($class) { if ('Composer\Autoload\ClassLoader' === $class) { require __DIR__ . '/ClassLoader.php'; } } /** * @return \Composer\Autoload\ClassLoader */ public static function getLoader() { if (null !== self::$loader) { return self::$loader; } require __DIR__ . '/platform_check.php'; spl_autoload_register(array('ComposerAutoloaderInit182e75197e9252636a5a80347e29c708', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); spl_autoload_unregister(array('ComposerAutoloaderInit182e75197e9252636a5a80347e29c708', 'loadClassLoader')); require __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInit182e75197e9252636a5a80347e29c708::getInitializer($loader)); $loader->register(true); return $loader; } } <?php // autoload_static.php @generated by Composer namespace Composer\Autoload; class ComposerStaticInit182e75197e9252636a5a80347e29c708 { public static $prefixLengthsPsr4 = array ( 'g' => array ( 'geoPHP\\' => 7, ), ); public static $prefixDirsPsr4 = array ( 'geoPHP\\' => array ( 0 => __DIR__ . '/..' . '/wgirhad/geophp/src', ), ); public static $classMap = array ( 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', ); public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { $loader->prefixLengthsPsr4 = ComposerStaticInit182e75197e9252636a5a80347e29c708::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInit182e75197e9252636a5a80347e29c708::$prefixDirsPsr4; $loader->classMap = ComposerStaticInit182e75197e9252636a5a80347e29c708::$classMap; }, null, ClassLoader::class); } } { "packages": [ { "name": "wgirhad/geophp", "version": "v3.0.0", "version_normalized": "3.0.0.0", "source": { "type": "git", "url": "https://github.com/wgirhad/geoPHP.git", "reference": "74f5b8bdc851cb92b8c07df99a437245f3878fcd" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/wgirhad/geoPHP/zipball/74f5b8bdc851cb92b8c07df99a437245f3878fcd", "reference": "74f5b8bdc851cb92b8c07df99a437245f3878fcd", "shasum": "" }, "require": { "php": "^7.1|^8.0" }, "provide": { "geophp/geophp": "3.0.0" }, "require-dev": { "php-coveralls/php-coveralls": "2.*", "phpbench/phpbench": "^1.0", "phpstan/phpstan": "^1.4.0", "phpunit/phpunit": "^8.0|^9.0", "squizlabs/php_codesniffer": "3.*" }, "suggest": { "ext-geos": "GEOS allows more advanced operations" }, "time": "2023-02-24T12:14:17+00:00", "type": "library", "installation-source": "dist", "autoload": { "psr-4": { "geoPHP\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later", "BSD-3-Clause" ], "authors": [ { "name": "Patrick Hayes", "role": "Original creator" }, { "name": "Péter Báthory", "role": "Developer" }, { "name": "Willian Girhad", "role": "Developer" } ], "description": "Open-source native PHP library for doing geometry operations. Can read and write a wide variety of formats: (E)WKT, (E)WKB, TWKB, GeoJSON, KML, GPX, GeoRSS. Works with all Simple-Feature geometries (Point, LineString, Polygon...) and can be used to get centroids, bounding-boxes, area, etc.", "homepage": "https://github.com/wgirhad/geoPHP", "keywords": [ "Polygon", "converter", "geoPHP", "geojson", "geometry", "gis", "gpx", "kml", "linestring", "twkb", "wkb", "wkt" ], "support": { "source": "https://github.com/wgirhad/geoPHP/tree/v3.0.0" }, "install-path": "../wgirhad/geophp" } ], "dev": true, "dev-package-names": [] } <?php return array( 'root' => array( 'name' => '__root__', 'pretty_version' => '1.0.0+no-version-set', 'version' => '1.0.0.0', 'reference' => null, 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev' => true, ), 'versions' => array( '__root__' => array( 'pretty_version' => '1.0.0+no-version-set', 'version' => '1.0.0.0', 'reference' => null, 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev_requirement' => false, ), 'geophp/geophp' => array( 'dev_requirement' => false, 'provided' => array( 0 => '3.0.0', ), ), 'wgirhad/geophp' => array( 'pretty_version' => 'v3.0.0', 'version' => '3.0.0.0', 'reference' => '74f5b8bdc851cb92b8c07df99a437245f3878fcd', 'type' => 'library', 'install_path' => __DIR__ . '/../wgirhad/geophp', 'aliases' => array(), 'dev_requirement' => false, ), ), ); <?php // platform_check.php @generated by Composer $issues = array(); if (!(PHP_VERSION_ID >= 70100)) { $issues[] = 'Your Composer dependencies require a PHP version ">= 7.1.0". You are running ' . PHP_VERSION . '.'; } if ($issues) { if (!headers_sent()) { header('HTTP/1.1 500 Internal Server Error'); } if (!ini_get('display_errors')) { if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); } elseif (!headers_sent()) { echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; } } trigger_error( 'Composer detected issues in your platform: ' . implode(' ', $issues), E_USER_ERROR ); } <?php declare(strict_types=1); namespace geoPHP\Geometry; use geoPHP\Exception\InvalidGeometryException; /** * A Surface is a 2-dimensional abstract geometric object. * * OGC 06-103r4 6.1.10 specification: * A simple Surface may consists of a single “patch” that is associated with one “exterior boundary” and 0 or more * “interior” boundaries. A single such Surface patch in 3-dimensional space is isometric to planar Surfaces, by a * simple affine rotation matrix that rotates the patch onto the plane z = 0. If the patch is not vertical, the * projection onto the same plane is an isomorphism, and can be represented as a linear transformation, i.e. an affine. * * @package geoPHP\Geometry */ abstract class Surface extends Collection { /** * Checks and stores geometry components. * * @param Curve[]|Polygon[] $components Array of Curve or Polygon components. * @param string $allowedComponentType A class the components must be instance of. Default: Geometry. * * @throws InvalidGeometryException */ public function __construct( array $components = [], string $allowedComponentType = Geometry::class ) { parent::__construct($components, $allowedComponentType); } public function geometryType(): string { return Geometry::SURFACE; } public function dimension(): int { return 2; } /** * Returns true if the Surface represents the empty set. * * A Surface is empty if and only if it has no components. * * @return bool */ public function isEmpty(): bool { return count($this->components) === 0; } public function startPoint(): ?Point { return null; } public function endPoint(): ?Point { return null; } public function pointN(int $n): ?Point { return null; } public function isClosed(): ?bool { return null; } public function isRing(): ?bool { return null; } public function length(): float { return 0.0; } public function length3D(): float { return 0.0; } public function haversineLength(): float { return 0.0; } public function vincentyLength(): float { return 0.0; } public function greatCircleLength(float $radius = null): float { return 0.0; } public function minimumZ(): ?float { return null; } public function maximumZ(): ?float { return null; } public function minimumM(): ?float { return null; } public function maximumM(): ?float { return null; } public function elevationGain(float $verticalTolerance = 0.0): ?float { return null; } public function elevationLoss(float $verticalTolerance = 0.0): ?float { return null; } public function zDifference(): ?float { return null; } } <?php declare(strict_types=1); namespace geoPHP\Geometry; use geoPHP\Exception\InvalidGeometryException; use function gettype; use function is_finite; use function is_numeric; use function sqrt; /** * A Point is a 0-dimensional geometric object and represents a single location in coordinate space. * A Point has an x-coordinate value, a y-coordinate value. * If called for by the associated Spatial Reference System, it may also have coordinate values for z and m. * * @phpstan-consistent-constructor */ class Point extends Geometry { /** * @var float|null */ protected $x = null; /** * @var float|null */ protected $y = null; /** * @var float|null */ protected $z = null; /** * @var float|null */ protected $m = null; /** * Checks and stores coordinates. * * @param int|float|string|null $x The x coordinate (or longitude) * @param int|float|string|null $y The y coordinate (or latitude) * @param int|float|string|null $z The z coordinate (or altitude) - optional * @param int|float|string|null $m Measure - optional * * @throws InvalidGeometryException */ public function __construct($x = null, $y = null, $z = null, $m = null) { // If both X and Y is null, than it is an empty point if ($x === null && $y === null) { return; } // Basic validation: x and y must be numeric and finite (non NaN). if (!is_numeric($x) || !is_numeric($y) || !is_finite((float) $x) || !is_finite((float) $y)) { throw new InvalidGeometryException( 'Cannot construct Point, x and y must be numeric and finite, ' . gettype($x) . ' given.' ); } // Convert to float in case they are passed in as a string or integer etc. $this->x = (float) $x; $this->y = (float) $y; // Check to see if this point has Z (height) value if ($z !== null) { if (!is_numeric($z) || !is_finite((float) $z)) { throw new InvalidGeometryException( 'Cannot construct Point, z must be numeric and finite, ' . gettype($z) . ' given.' ); } $this->z = (float) $z; } // Check to see if this is a measure if ($m !== null) { if (!is_numeric($m) || !is_finite((float) $m)) { throw new InvalidGeometryException( 'Cannot construct Point, m must be numeric and finite, ' . gettype($m) . ' given.' ); } $this->m = (float) $m; } } /** * * Creates a Point from array of coordinates * * @param array<float|int|null> $coordinateArray Array of coordinates. * * @throws InvalidGeometryException * * @return Point */ public static function fromArray(array $coordinateArray): Point { [$x, $y, $z, $m] = array_merge($coordinateArray, [null, null, null, null]); return new static($x, $y, $z, $m); } public function geometryType(): string { return Geometry::POINT; } public function dimension(): int { return 0; } /** * Get X (longitude) coordinate * * @return float|null The X coordinate */ public function x(): ?float { return $this->x; } /** * Returns Y (latitude) coordinate * * @return float|null The Y coordinate */ public function y(): ?float { return $this->y; } /** * Returns Z (altitude) coordinate * * @return float|null The Z coordinate or NULL is not a 3D point */ public function z(): ?float { return $this->z; } /** * Returns M (measured) value * * @return float|null The measured value */ public function m(): ?float { return $this->m; } public function is3D(): bool { return $this->z !== null; } public function isMeasured(): bool { return $this->m !== null; } /** * Inverts x and y coordinates * Useful with old applications still using lng lat * * @return self * */ public function invertXY(): self { $tempX = $this->x; $this->x = $this->y; $this->y = $tempX; $this->flushGeosCache(); return $this; } /** * Centroid of a point is itself * * @return self */ public function centroid(): self { return $this; } /** * @return array{maxy: ?float, miny: ?float, maxx: ?float, minx: ?float} */ public function getBBox(): array { return [ 'maxy' => $this->y(), 'miny' => $this->y(), 'maxx' => $this->x(), 'minx' => $this->x(), ]; } /** * @return array{}|array{float, float}|array{float, float, float}|array{float, float, float|null, float} */ public function asArray(): array { if ($this->isEmpty()) { return []; } if (!$this->is3D() && !$this->isMeasured()) { return [$this->x, $this->y]; } if ($this->is3D() && $this->isMeasured()) { return [$this->x, $this->y, $this->z, $this->m]; } if ($this->is3D()) { return [$this->x, $this->y, $this->z]; } // if isMeasured return [$this->x, $this->y, null, $this->m]; } /** * The boundary of a Point is the empty set. * * @return GeometryCollection */ public function boundary(): ?Geometry { return new GeometryCollection(); } /** * @return bool */ public function isEmpty(): bool { return $this->x === null; } /** * Returns 1 if point is not empty, or 0 if empty. * * OGC SFA defines numPoints() only for LineStrings, but PostGIS implements it for all geometries. * PostGIS' approach seems more useful, so we follow their standard. * PostGIS doesn't take empty points into acount. * * Note: Behaviour of this method has changed in version 2.1. * * @return int */ public function numPoints(): int { return $this->isEmpty() ? 0 : 1; } /** * @return Point[] */ public function getPoints(): array { return [$this]; } /** * @return Point[] */ public function getComponents(): array { return [$this]; } /** * Determines weather the specified geometry is spatially equal to this Point * * Because of limited floating point precision in PHP, equality can be only approximated * @see: http://php.net/manual/en/function.bccomp.php * @see: http://php.net/manual/en/language.types.float.php * * @param Geometry $geometry * * @return bool */ public function equals(Geometry $geometry): bool { return $geometry->geometryType() === Geometry::POINT ? (abs($this->x() - $geometry->x()) <= 1.0E-9 && abs($this->y() - $geometry->y()) <= 1.0E-9) : false; } public function isSimple(): bool { return true; } public function flatten(): void { $this->z = null; $this->m = null; $this->flushGeosCache(); } /** * @param Geometry $geometry * @return float|null */ public function distance(Geometry $geometry): ?float { if ($this->isEmpty() || $geometry->isEmpty()) { return null; } if ($this->getGeos()) { // @codeCoverageIgnoreStart /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->distance($geometry->getGeos()); // @codeCoverageIgnoreEnd } if ($geometry->geometryType() == Geometry::POINT) { return sqrt( ($this->x() - $geometry->x()) ** 2 + ($this->y() - $geometry->y()) ** 2 ); } if ($geometry instanceof MultiGeometry) { $distance = null; foreach ($geometry->getComponents() as $component) { $checkDistance = $this->distance($component); if ($checkDistance === 0.0) { return 0.0; } if ($checkDistance === null) { continue; } if ($distance === null || $checkDistance < $distance) { $distance = $checkDistance; } } return $distance; } else { // For LineString, Polygons, MultiLineString and MultiPolygon. the nearest point might be a vertex, // but it could also be somewhere along a line-segment that makes up the geometry (between vertices). // Here we brute force check all line segments that make up these geometries $distance = null; foreach ($geometry->explode(true) as $seg) { // As per http://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment // and http://paulbourke.net/geometry/pointlineplane/ /** @var Point[] $seg */ $x1 = $seg[0]->x(); $y1 = $seg[0]->y(); $x2 = $seg[1]->x(); $y2 = $seg[1]->y(); $dx21 = $x2 - $x1; $dy21 = $y2 - $y1; $segNorm = ($dx21 * $dx21) + ($dy21 * $dy21); if ($segNorm == 0) { // Line-segment's endpoints are identical. This is merely a point masquerading as a line-segment. $componentDistance = $this->distance($seg[1]); } else { $x3 = $this->x(); $y3 = $this->y(); $dx31 = $x3 - $x1; $dy31 = $y3 - $y1; $uNorm = (($dx31 * $dx21) + ($dy31 * $dy21)) / $segNorm; if ($uNorm >= 1) { $closestPointX = $x2; $closestPointY = $y2; } elseif ($uNorm <= 0) { $closestPointX = $x1; $closestPointY = $y1; } else { // Closest point is between p1 and p2 $closestPointX = $x1 + ($uNorm * $dx21); $closestPointY = $y1 + ($uNorm * $dy21); } $dx = $closestPointX - $x3; $dy = $closestPointY - $y3; $componentDistance = sqrt(($dx * $dx) + ($dy * $dy)); } if ($componentDistance === 0.0) { return 0.0; } if ($distance === null || $componentDistance < $distance) { $distance = $componentDistance; } } return $distance; } } public function minimumZ(): ?float { return $this->z(); } public function maximumZ(): ?float { return $this->z(); } public function minimumM(): ?float { return $this->m(); } public function maximumM(): ?float { return $this->m(); } /* The following methods are not valid for this geometry type */ public function area(): float { return 0.0; } public function length(): float { return 0.0; } public function length3D(): float { return 0.0; } public function greatCircleLength(float $radius = null): float { return 0.0; } public function haversineLength(): float { return 0.0; } public function vincentyLength(): float { return 0.0; } public function zDifference(): ?float { return null; } public function elevationGain(?float $verticalTolerance = 0.0): ?float { return null; } public function elevationLoss(?float $verticalTolerance = 0.0): ?float { return null; } public function numGeometries(): ?int { return null; } public function geometryN(int $n = null): ?Geometry { return null; } public function startPoint(): ?Point { return null; } public function endPoint(): ?Point { return null; } public function isRing(): ?bool { return null; } public function isClosed(): ?bool { return null; } public function pointN(int $n = null): ?Point { return null; } public function exteriorRing(): ?LineString { return null; } public function numInteriorRings(): ?int { return null; } public function interiorRingN(int $n = null): ?LineString { return null; } /** * @param bool $toArray * @return null */ public function explode(bool $toArray = false): ?array // @phpstan-ignore-line { return null; } } <?php declare(strict_types=1); namespace geoPHP\Geometry; use geoPHP\Exception\InvalidGeometryException; use geoPHP\geoPHP; /** * A MultiCurve is a 1-dimensional collection whose elements are Curves. * * MultiCurve is a non-instantiable class; it defines a set of methods for its subclasses and is * included for reasons of extensibility. * * @see OGC SFA 6.1.8.1 * * @method Curve[] getComponents() * @property Curve[] $components */ abstract class MultiCurve extends MultiGeometry { /** * Checks and stores geometry components. * * @param Curve[] $components Array of Curve components. * @param string $allowedComponentType A class the components must be instance of. Default: Curve. * * @throws InvalidGeometryException */ public function __construct( array $components = [], string $allowedComponentType = Curve::class ) { parent::__construct($components, $allowedComponentType); } public function geometryType(): string { return Geometry::MULTI_CURVE; } /** * A MultiCurve is a 1-dimensional collection. * * @return int */ public function dimension(): int { return 1; } /** * MultiCurve is closed if all it's components are closed. * * @see OGC SFA 6.1.8.1 * * @return bool */ public function isClosed(): bool { if ($this->isEmpty()) { return false; } foreach ($this->getComponents() as $line) { if (!$line->isClosed()) { return false; } } return true; } /** * The boundary of a MultiCurve is obtained by applying the “mod 2” union rule: * A Point is in the boundary of a MultiCurve if it is in the boundaries of an odd number * of elements of the MultiCurve. * The boundary of a closed MultiCurve is always empty. * * @see OGC SFA 6.1.8.1 * * @return Geometry|null */ public function boundary(): ?Geometry { if (geoPHP::isGeosInstalled()) { // @codeCoverageIgnoreStart return geoPHP::geosToGeometry($this->getGeos()->boundary()); // @codeCoverageIgnoreEnd } $points = []; foreach ($this->components as $line) { if (!$line->isEmpty() && !$line->isClosed()) { if (count($points) && $line->startPoint()->equals($points[count($points) - 1])) { array_pop($points); } else { $points[] = $line->startPoint(); } $points[] = $line->endPoint(); } } return new MultiPoint($points); } /** * A MultiCurve is simple if and only if all of its elements are simple and the only intersections * between any two elements occur at Points that are on the boundaries of both elements. * * @see OGC SFA 6.1.8.1 * * @return bool */ public function isSimple(): bool { // @codeCoverageIgnoreStart return parent::isSimple(); // @codeCoverageIgnoreEnd } } <?php declare(strict_types=1); namespace geoPHP\Geometry; use geoPHP\Exception\UnsupportedMethodException; use geoPHP\geoPHP; use GEOSGeometry; use function array_key_exists; use function array_unshift; use function call_user_func_array; use function is_array; use function str_replace; use function strstr; use function strtolower; /** * Geometry is the root class of the hierarchy. Geometry is an abstract (non-instantiable) class. * * OGC 06-103r4 6.1.2 specification: * The instantiable subclasses of Geometry defined in this Standard are restricted to * 0, 1 and 2-dimensional geometric objects that exist in 2, 3 or 4-dimensional coordinate space. * * Geometry values in R^2 have points with coordinate values for x and y. * Geometry values in R^3 have points with coordinate values for x, y and z or for x, y and m. * Geometry values in R^4 have points with coordinate values for x, y, z and m. * The interpretation of the coordinates is subject to the coordinate reference systems associated to the point. * All coordinates within a geometry object should be in the same coordinate reference systems. * Each coordinate shall be unambiguously associated to a coordinate reference system * either directly or through its containing geometry. * * The z coordinate of a point is typically, but not necessarily, represents altitude or elevation. * The m coordinate represents a measurement. */ abstract class Geometry { // ----------------------------------------------- // // Type constants // // ----------------------------------------------- // const POINT = 'Point'; const LINE_STRING = 'LineString'; const POLYGON = 'Polygon'; const MULTI_POINT = 'MultiPoint'; const MULTI_LINE_STRING = 'MultiLineString'; const MULTI_POLYGON = 'MultiPolygon'; const GEOMETRY_COLLECTION = 'GeometryCollection'; const CIRCULAR_STRING = 'CircularString'; const COMPOUND_CURVE = 'CompoundCurve'; const CURVE_POLYGON = 'CurvePolygon'; const MULTI_CURVE = 'MultiCurve'; // Abstract const MULTI_SURFACE = 'MultiSurface'; // Abstract const CURVE = 'Curve'; // Abstract const SURFACE = 'Surface'; // Abstract const POLYHEDRAL_SURFACE = 'PolyhedralSurface'; const TIN = 'TIN'; const TRIANGLE = 'Triangle'; /** @var int|null $srid Spatial Reference System Identifier (http://en.wikipedia.org/wiki/SRID) */ protected $srid = null; /** * @var mixed|null Custom (meta)data */ protected $data; /** * @var \GEOSGeometry|null Caches the GEOSGeometry representation of this Geometry. */ private $geos = null; // ----------------------------------------------- // // Basic methods on geometric objects // // ----------------------------------------------- // /** * Returns the name of the instantiable subtype of Geometry of which the geometric object is an instantiable member. * * @return string */ abstract public function geometryType(): string; /** * The inherent dimension of the geometric object, which must be less than or equal to the coordinate dimension. * In non-homogeneous collections, this will return the largest topological dimension of the contained objects. * * @return int */ abstract public function dimension(): int; /** * Returns true if the geometric object is the empty Geometry. * If true, then the geometric object represents the empty point set ∅ for the coordinate space. * * @return bool */ abstract public function isEmpty(): bool; /** * Returns true if the geometric object has no anomalous geometric points, * such as self intersection or self tangency. * * The description of each instantiable geometric class will include the specific conditions * that cause an instance of that class to be classified as not simple * * @return bool|null */ abstract public function isSimple(): ?bool; /** * Returns the closure of the combinatorial boundary of the geometric object. * * @return Geometry|null */ abstract public function boundary(): ?Geometry; /** * Get all sub-geometry components of the geometry. * * @return Geometry[] */ abstract public function getComponents(): array; // ----------------------------------------------- // // Methods applicable on certain geometry types // // ----------------------------------------------- // /** * The x-coordinate value of the Point. * * @return float|null */ abstract public function x(): ?float; /** * The y-coordinate value of the Point. * * @return float|null */ abstract public function y(): ?float; /** * The z-coordinate value (elevation) of the Point. * * @return float|null */ abstract public function z(): ?float; /** * The m-coordinate value (measure) of the Point. * * @return float|null */ abstract public function m(): ?float; /** * Returns TRUE if this geometric object has z coordinate values. * * @return bool */ abstract public function is3D(): bool; /** * Returns TRUE if this geometric object has m coordinate values. * * @return bool */ abstract public function isMeasured(): bool; /** * The area of the Polygon (or GeometryCollection), as measured in the spatial reference system of the geometry. * * @return float */ abstract public function area(): float; /** * The mathematical centroid of the geometry as a Point. * For polygons, the result is not guaranteed to be interior. * * @return Point */ abstract public function centroid(): Point; /** * The length of a Curve in its associated spatial reference. * * @return float */ abstract public function length(): float; /** * The 3D length of a Curve in its associated spatial reference. * * @return float */ abstract public function length3D(): float; /** * The number of component geometries in the collection. * * @return int|null */ abstract public function numGeometries(): ?int; /** * Returns the geometry N. in the collection. Note that the index starts at 1. * * @param int $n 1-based index. * * @return Geometry|null The geometry, or null if not found. */ abstract public function geometryN(int $n): ?Geometry; /** * The first Point of the LineString. * * @return Point|null */ abstract public function startPoint(): ?Point; /** * The last Point of the LineString. * * @return Point|null */ abstract public function endPoint(): ?Point; /** * Returns TRUE if the Curve isClosed() and isSimple(). * * @return bool|null */ abstract public function isRing(): ?bool; /** * Returns TRUE if the startPoint() and endPoint() of the Curve equals. * * @return bool|null */ abstract public function isClosed(): ?bool; /** * The number of Points in the Geometry. * * @return int */ abstract public function numPoints(): int; /** * Returns the specified N-th Point in the Geometry. Note that the index starts at 1. * * @param int $n N-th point * @return Point|null */ abstract public function pointN(int $n): ?Point; /** * Returns the exterior ring of the Polygon. * * @return LineString|null */ abstract public function exteriorRing(): ?LineString; /** * Returns the number of interior rings in the Polygon. * * @return int|null */ abstract public function numInteriorRings(): ?int; /** * Returns the N-th interior ring in the Polygon as a LineString. Note that the index starts at 1. * * @param int $n * @return LineString|null */ abstract public function interiorRingN(int $n): ?LineString; /** * The distance of this geometry to another geometry in their associated spatial reference. * * @return float|null */ abstract public function distance(Geometry $geom): ?float; /** * Returns TRUE if this geometry is "spatially equal" to other geometry. * * @param Geometry $geometry * * @return bool */ abstract public function equals(Geometry $geometry): bool; // ----------------------------------------------- // // Abstract Non-Standard Methods // // ----------------------------------------------- // /** * The minimum bounding box of the Geometry as array. * * @see envelope() * * @return array{maxy: float, miny: float, maxx: float, minx: float}|null * Array of min and max values of x and y coordinates. */ abstract public function getBBox(): ?array; /** * Get the given geometry as an array of components (recursive) * * @return array<mixed> */ abstract public function asArray(): array; /** * Get all the points of the geometry. * * @return Point[] */ abstract public function getPoints(): array; /** * Swaps X and Y coordinates of the geometry. * * Useful to fix geometries with lat-lng coordinate order. * * @return self */ abstract public function invertXY(); /** * Removes 3D information and measures from the geometry. * * @return void */ abstract public function flatten(): void; /** * Get all line segments. * * @param bool $toArray Return segments as LineString or array of start and end points. Explode(true) is faster. * * @return array<mixed>|null Returns line segments or null for 0-deminsional geometries. */ abstract public function explode(bool $toArray = false): ?array; abstract public function greatCircleLength(float $radius = geoPHP::EARTH_WGS84_SEMI_MAJOR_AXIS): float; //meters abstract public function haversineLength(): float; //degrees abstract public function vincentyLength(): float; // Elevations statistics abstract public function minimumZ(): ?float; abstract public function maximumZ(): ?float; abstract public function minimumM(): ?float; abstract public function maximumM(): ?float; abstract public function zDifference(): ?float; abstract public function elevationGain(float $verticalTolerance = 0.0): ?float; abstract public function elevationLoss(float $verticalTolerance = 0.0): ?float; // ----------------------------------------------- // // Standard – Common to all geometries // // ----------------------------------------------- // /** * Returns the Spatial Reference System ID for this geometric object. * * @return int|null */ public function getSRID(): ?int { return $this->srid; } /** * Set (or removes) the Spatial Reference System ID for this geometric object. * * @param int|null $srid Spatial Reference System Identifier. */ public function setSRID(?int $srid): void { if ($this->getGeos()) { // @codeCoverageIgnoreStart $this->getGeos()->setSRID($srid ?: 0); // @codeCoverageIgnoreEnd } $this->srid = $srid; } /** * Adds custom data to the geometry. * * @param string|array<mixed> $property The name of the data or an associative array. * @param mixed|null $value The data. Can be of any type (string, integer, array, etc.). */ public function setData($property, $value = null): void { if (is_array($property)) { $this->data = $property; } else { $this->data[$property] = $value; } } /** * Returns the requested data by property name, or all data of the geometry. * * @param string|null $property The name of the data. If omitted, all data will be returned. * * @return mixed|null The data or null if not exists. */ public function getData(string $property = null) { if ($property) { return $this->hasDataProperty($property) ? $this->data[$property] : null; } else { return $this->data; } } /** * Tells whether the geometry has data with the specified name. * * @param string $property The name of the property. * * @return bool True if the geometry has data with the specified name. */ public function hasDataProperty(string $property): bool { return array_key_exists($property, $this->data ?: []); } /** * The minimum bounding box of the Geometry as Geometry. * * @see getBBox() * * @return Geometry */ public function envelope(): Geometry { if ($this->isEmpty()) { $type = 'geoPHP\\Geometry\\' . $this->geometryType(); /** @var Geometry */ $emptyGeometry = new $type(); return $emptyGeometry; } if ($this->geometryType() === Geometry::POINT) { return $this; } if ($this->getGeos()) { // @codeCoverageIgnoreStart /** @noinspection PhpUndefinedMethodInspection */ return geoPHP::geosToGeometry($this->getGeos()->envelope()); // @codeCoverageIgnoreEnd } $boundingBox = $this->getBBox(); if ($boundingBox) { $points = [ new Point($boundingBox['maxx'], $boundingBox['miny']), new Point($boundingBox['maxx'], $boundingBox['maxy']), new Point($boundingBox['minx'], $boundingBox['maxy']), new Point($boundingBox['minx'], $boundingBox['miny']), new Point($boundingBox['maxx'], $boundingBox['miny']), ]; return new Polygon([new LineString($points)]); } else { return new Polygon(); } } // ----------------------------------------------- // // Non-Standard methods – Common to all geometries // // ----------------------------------------------- // /** * Outputs the geometry into the specified adapter format. * * @param string $format A file format or adapter name. E.g.: "GPX", or "GeoJSON". * @param mixed ...$args Additional adapter specific parameters. * * @return string */ public function out(string $format, ...$args): string { $format = strtolower($format); if (strstr($format, 'xdr')) { //Big Endian WKB $args[] = true; $format = str_replace('xdr', '', $format); } $processorType = 'geoPHP\\Adapter\\' . geoPHP::getAdapterMap()[$format]; $processor = new $processorType(); array_unshift($args, $this); return call_user_func_array([$processor, 'write'], $args); } public function __toString(): string { return $this->out('ewkt'); } public function asText(): string { return (string) $this; } public function asBinary(): string { return $this->out('wkb'); } public function coordinateDimension(): int { return 2 + ($this->z() ? 1 : 0) + ($this->isMeasured() ? 1 : 0); } /** * Utility function to check if any line segments intersect * Derived from: * @source http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect * * @param Point $segment1Start * @param Point $segment1End * @param Point $segment2Start * @param Point $segment2End * @return bool */ public static function segmentIntersects( Point $segment1Start, Point $segment1End, Point $segment2Start, Point $segment2End ): bool { $p0x = $segment1Start->x(); $p0y = $segment1Start->y(); $p1x = $segment1End->x(); $p1y = $segment1End->y(); $p2x = $segment2Start->x(); $p2y = $segment2Start->y(); $p3x = $segment2End->x(); $p3y = $segment2End->y(); $s1x = $p1x - $p0x; $s1y = $p1y - $p0y; $s2x = $p3x - $p2x; $s2y = $p3y - $p2y; $fps = (-$s2x * $s1y) + ($s1x * $s2y); $fpt = (-$s2x * $s1y) + ($s1x * $s2y); if ($fps == 0 || $fpt == 0) { return false; } $s = (-$s1y * ($p0x - $p2x) + $s1x * ($p0y - $p2y)) / $fps; $t = ($s2x * ($p0y - $p2y) - $s2y * ($p0x - $p2x)) / $fpt; // Return true if collision is detected return ($s > 0 && $s < 1 && $t > 0 && $t < 1); } // ----------------------------------------------- // // Aliases // // ----------------------------------------------- // /** * @deprecated 2.1 */ public function hasZ(): bool { return $this->is3D(); } /** * @deprecated 2.1 */ public function getX(): ?float { return $this->x(); } /** * @deprecated 2.1 */ public function getY(): ?float { return $this->y(); } /** * @deprecated 2.1 */ public function getZ(): ?float { return $this->z(); } /** * @deprecated 2.1 */ public function getM(): ?float { return $this->m(); } /** * @return array<string, float> * @deprecated 2.1 */ public function getBoundingBox(): ?array { return $this->getBBox(); } /** * @return array<mixed> * @deprecated 2.1 */ public function dump(): array { return $this->getComponents(); } /** * @deprecated 2.1 */ public function getCentroid(): ?Point { return $this->centroid(); } /** * @deprecated 2.1 */ public function getArea(): ?float { return $this->area(); } /** * @deprecated 2.1 */ public function geos(): ?GEOSGeometry { return $this->getGeos(); } /** * @deprecated 2.1 */ public function getGeomType(): string { return $this->geometryType(); } /** * @deprecated 2.1 */ public function SRID(): ?int { return $this->getSRID(); } // ----------------------------------------------- // // GEOS Only Functions // // ----------------------------------------------- // /** * Returns the GEOS representation of Geometry if GEOS is installed. * * GEOS supports SRID and Z-coordinate (3D), but lacks support of M-coordinate. * * @return \GEOSGeometry|null * @codeCoverageIgnore */ public function getGeos(): ?\GEOSGeometry { // If it's already been set, just return it if ($this->geos && geoPHP::isGeosInstalled()) { return $this->geos; } // It hasn't been set yet, generate it if (geoPHP::isGeosInstalled()) { /** @noinspection PhpUndefinedClassInspection */ $reader = new \GEOSWKBReader(); /** @noinspection PhpUndefinedMethodInspection */ $this->geos = $reader->read($this->out('ewkb')); } else { $this->geos = null; } return $this->geos; } /** * Resets internal GEOSGeometry cache to null. Useful if Geometry has changed. */ public function flushGeosCache(): void { $this->geos = null; } /** * @return Geometry|Point * * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function pointOnSurface(): Geometry { if ($this->isEmpty()) { return new Point(); } if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return geoPHP::geosToGeometry($this->getGeos()->pointOnSurface()); } // help for implementation: http://gis.stackexchange.com/questions/76498/how-is-st-pointonsurface-calculated throw UnsupportedMethodException::geos(__METHOD__); } /** * @param Geometry $geometry * @return bool * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function equalsExact(Geometry $geometry): bool { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->equalsExact($geometry->getGeos()); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param Geometry $geometry * @param string $pattern * @return string|bool * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function relate(Geometry $geometry, string $pattern = null) { if ($this->getGeos()) { if ($pattern) { /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->relate($geometry->getGeos(), $pattern); } else { /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->relate($geometry->getGeos()); } } throw UnsupportedMethodException::geos(__METHOD__); } /** * @return array{valid: bool, reason?: string, location?: GEOSGeometry} * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function checkValidity(): array { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->checkValidity(); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param float $distance * @return Geometry * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function buffer(float $distance): Geometry { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return geoPHP::geosToGeometry($this->getGeos()->buffer($distance)); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param Geometry $geometry * @return Geometry * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function intersection(Geometry $geometry): Geometry { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return geoPHP::geosToGeometry($this->getGeos()->intersection($geometry->getGeos())); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @return Geometry * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function convexHull(): Geometry { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return geoPHP::geosToGeometry($this->getGeos()->convexHull()); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param Geometry $geometry * @return Geometry * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function difference(Geometry $geometry): Geometry { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return geoPHP::geosToGeometry($this->getGeos()->difference($geometry->getGeos())); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param Geometry $geometry * @return Geometry * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function symDifference(Geometry $geometry): Geometry { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return geoPHP::geosToGeometry($this->getGeos()->symDifference($geometry->getGeos())); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param Geometry|Geometry[] $geometry * @return Geometry * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function union($geometry): Geometry { if ($this->getGeos()) { if (is_array($geometry)) { $geom = $this->getGeos(); foreach ($geometry as $item) { /** @noinspection PhpUndefinedMethodInspection */ $geom = $geom->union($item->getGeos()); } return geoPHP::geosToGeometry($geom); } else { /** @noinspection PhpUndefinedMethodInspection */ return geoPHP::geosToGeometry($this->getGeos()->union($geometry->getGeos())); } } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param float $tolerance * @param bool|false $preserveTopology * @return Geometry * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function simplify(float $tolerance, bool $preserveTopology = false): Geometry { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return geoPHP::geosToGeometry($this->getGeos()->simplify($tolerance, $preserveTopology)); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @return Geometry|null * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function makeValid() { if ($this->getGeos()) { /** @phpstan-ignore-next-line */ return geoPHP::geosToGeometry($this->getGeos()->makeValid()); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @return Geometry|null * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function buildArea() { if ($this->getGeos()) { /** @phpstan-ignore-next-line */ return geoPHP::geosToGeometry($this->getGeos()->buildArea()); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param Geometry $geometry * @return bool * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function disjoint(Geometry $geometry): bool { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->disjoint($geometry->getGeos()); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param Geometry $geometry * @return bool * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function touches(Geometry $geometry): bool { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->touches($geometry->getGeos()); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param Geometry $geometry * @return bool * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function intersects(Geometry $geometry): bool { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->intersects($geometry->getGeos()); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param Geometry $geometry * @return bool * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function crosses(Geometry $geometry): bool { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->crosses($geometry->getGeos()); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param Geometry $geometry * @return bool * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function within(Geometry $geometry): bool { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->within($geometry->getGeos()); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param Geometry $geometry * @return bool * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function contains(Geometry $geometry): bool { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->contains($geometry->getGeos()); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param Geometry $geometry * @return bool * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function overlaps(Geometry $geometry): bool { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->overlaps($geometry->getGeos()); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param Geometry $geometry * @return bool * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function covers(Geometry $geometry): bool { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->covers($geometry->getGeos()); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param Geometry $geometry * @return bool * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function coveredBy(Geometry $geometry): bool { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->coveredBy($geometry->getGeos()); } throw UnsupportedMethodException::geos(__METHOD__); } /** * @param Geometry $geometry * @return float * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function hausdorffDistance(Geometry $geometry): float { if ($this->getGeos()) { /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->hausdorffDistance($geometry->getGeos()); } throw UnsupportedMethodException::geos(__METHOD__); } /** * Returns the distance from the origin of the geometry (LineString or MultiLineString) * to the point projected on the geometry (that is to a point of the line the closest to the given point). * * @param Geometry $point * @param bool $normalized Return the distance as a percentage between 0 (origin) and 1 (endpoint). * * @return float * @throws UnsupportedMethodException * @codeCoverageIgnore */ public function project(Geometry $point, bool $normalized = false): float { if ($this->getGeos()) { /** @phpstan-ignore-next-line */ return $this->getGeos()->project($point->getGeos(), $normalized); } throw UnsupportedMethodException::geos(__METHOD__); } } <?php declare(strict_types=1); namespace geoPHP\Geometry; use geoPHP\geoPHP; use geoPHP\Exception\InvalidGeometryException; /** * A MultiPoint is a 0-dimensional Collection. * The elements of a MultiPoint are restricted to Points. * The Points are not connected or ordered in any semantically important way. * A MultiPoint is simple if no two Points in the MultiPoint are equal (have identical coordinate values in X and Y). * Every MultiPoint is spatially equal under the definition in OGC 06-103r4 Clause 6.1.15.3 to a simple Multipoint. * * @method Point[] getComponents() * @property Point[] $components The elements of a MultiPoint are Points * * @phpstan-consistent-constructor */ class MultiPoint extends MultiGeometry { /** * Checks and stores geometry components. * * @param Point[] $components Array of Point components. * * @throws InvalidGeometryException */ public function __construct(array $components = []) { parent::__construct($components, Point::class); } /** * * Creates a MultiPoint from array of coordinates * * @param array{}|array<array{}|array<float|int|null>> $coordinateArray Multi-dimensional array of coordinates. * * @throws InvalidGeometryException * * @return MultiPoint */ public static function fromArray(array $coordinateArray): MultiPoint { $points = []; foreach ($coordinateArray as $point) { $points[] = Point::fromArray($point); } return new static($points); } /** * @return string */ public function geometryType(): string { return Geometry::MULTI_POINT; } /** * MultiPoint is 0-dimensional * @return int 0 */ public function dimension(): int { return 0; } /** * A MultiPoint is simple if no two Points in the MultiPoint are equal * (have identical coordinate values in X and Y). * * @return bool */ public function isSimple(): ?bool { $componentCount = count($this->components); for ($i = 0; $i < $componentCount; $i++) { for ($j = $i + 1; $j < $componentCount; $j++) { if ($this->components[$i]->equals($this->components[$j])) { return false; } } } return true; } /** * The boundary of a MultiPoint is the empty set. * @return GeometryCollection */ public function boundary(): ?Geometry { return new GeometryCollection(); } public function centroid(): Point { if ($this->isEmpty()) { return new Point(); } if ($this->getGeos()) { // @codeCoverageIgnoreStart return geoPHP::geosToGeometry($this->getGeos()->centroid()); // @codeCoverageIgnoreEnd } $x = 0; $y = 0; foreach ($this->getComponents() as $component) { $x += $component->x(); $y += $component->y(); } return new Point($x / $this->numPoints(), $y / $this->numPoints()); } // Not valid for this geometry type // -------------------------------- public function explode(bool $toArray = false): ?array // @phpstan-ignore-line { return null; } } <?php declare(strict_types=1); namespace geoPHP\Geometry; use geoPHP\Exception\InvalidGeometryException; use geoPHP\geoPHP; use function abs; use function array_merge; use function count; use function implode; use function max; use function min; /** * A Polygon is a planar Surface defined by 1 exterior boundary and 0 or more interior boundaries. * Each interior boundary defines a hole in the Polygon. * * @method LineString[] getComponents() * @method LineString|null geometryN(int $n) * @property LineString[] $components * * @phpstan-consistent-constructor */ class Polygon extends Surface { /** * Checks and stores geometry components. * * @param LineString[] $components Array of LineString components. * @param bool $forceCreate Force create polygon even if it's invalid because a ring is not closed. * Default is false. * @throws InvalidGeometryException */ public function __construct(array $components = [], bool $forceCreate = false) { parent::__construct($components, LineString::class); foreach ($this->getComponents() as $i => $component) { if ($component->numPoints() < 4) { throw new InvalidGeometryException( 'Cannot create Polygon: Invalid number of points in LinearRing. Found ' . $component->numPoints() . ', expected more than 3' ); } if (!$component->isClosed()) { if ($forceCreate) { $this->components[$i] = new LineString( array_merge($component->getComponents(), [$component->startPoint()]) ); } else { throw new InvalidGeometryException( 'Cannot create Polygon: contains non-closed ring (first point: ' . implode(' ', $component->startPoint()->asArray()) . ', last point: ' . implode(' ', $component->endPoint()->asArray()) . ')' ); } } } } /** * * Creates a Polygon from array of coordinates. * * @param array{}|array<array<array<float|int|null>>> $coordinateArray Multi-dimensional array of coordinates. * * @throws InvalidGeometryException * * @return Polygon */ public static function fromArray(array $coordinateArray): Polygon { $rings = []; foreach ($coordinateArray as $ring) { $rings[] = LineString::fromArray($ring); } return new static($rings); } public function geometryType(): string { return Geometry::POLYGON; } /** * @param bool|false $exteriorOnly Calculate the area of exterior ring only, or the polygon with holes * @param bool|false $signed Usually we want to get positive area, * but vertices order (CW or CCW) can be determined from signed area. * * @return float */ public function area(bool $exteriorOnly = false, bool $signed = false): float { if ($this->isEmpty()) { return 0.0; } if ($this->getGeos() && !$exteriorOnly) { // @codeCoverageIgnoreStart /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->area(); // @codeCoverageIgnoreEnd } $exteriorRing = $this->components[0]; $points = $exteriorRing->getComponents(); $pointCount = count($points); $a = 0.0; foreach ($points as $k => $p) { $j = ($k + 1) % $pointCount; $a = $a + ($p->x() * $points[$j]->y()) - ($p->y() * $points[$j]->x()); } $area = $signed ? ($a / 2) : abs(($a / 2)); if ($exteriorOnly) { return $area; } foreach ($this->components as $delta => $component) { if ($delta != 0) { $innerPoly = new Polygon([$component]); $area -= $innerPoly->area(); } } return $area; } /** * @return Point */ public function centroid(): Point { if ($this->isEmpty()) { return new Point(); } if ($this->getGeos()) { // @codeCoverageIgnoreStart return geoPHP::geosToGeometry($this->getGeos()->centroid()); // @codeCoverageIgnoreEnd } $x = 0; $y = 0; $totalArea = 0; foreach ($this->getComponents() as $i => $component) { $ca = $this->getRingCentroidAndArea($component); if ($i == 0) { $totalArea += $ca['area']; $x += $ca['x'] * $ca['area']; $y += $ca['y'] * $ca['area']; } else { $totalArea -= $ca['area']; $x += $ca['x'] * $ca['area'] * -1; $y += $ca['y'] * $ca['area'] * -1; } } if ($totalArea == 0.0) { return new Point(); } return new Point($x / $totalArea, $y / $totalArea); } /** * @param LineString $ring * @return array{area: float, x: ?float, y: ?float} */ protected function getRingCentroidAndArea(LineString $ring): array { $area = (new Polygon([$ring]))->area(true, true); $points = $ring->getPoints(); $pointCount = count($points); if ($pointCount === 0 || $area == 0.0) { return ['area' => 0.0, 'x' => null, 'y' => null]; } $x = 0; $y = 0; foreach ($points as $k => $point) { $j = ($k + 1) % $pointCount; $p = ($point->x() * $points[$j]->y()) - ($point->y() * $points[$j]->x()); $x += ($point->x() + $points[$j]->x()) * $p; $y += ($point->y() + $points[$j]->y()) * $p; } return ['area' => abs($area), 'x' => $x / (6 * $area), 'y' => $y / (6 * $area)]; } /** * Find the outermost point from the centroid. * * @returns Point The outermost point */ public function outermostPoint(): Point { $centroid = $this->centroid(); if ($centroid->isEmpty()) { return $centroid; } $maxDistance = 0.0; $maxPoint = null; foreach ($this->exteriorRing()->getPoints() as $point) { $distance = $centroid->distance($point); if ($distance > $maxDistance) { $maxDistance = $distance; $maxPoint = $point; } } return $maxPoint; } /** * Returns the exterior ring of the Polygon. * * @return LineString */ public function exteriorRing(): LineString { if ($this->isEmpty()) { return new LineString(); } return $this->components[0]; } /** * Returns the number of interior rings in the Polygon. * * @return int */ public function numInteriorRings(): int { if ($this->isEmpty()) { return 0; } return $this->numGeometries() - 1; } /** * Returns the Nth interior ring for the Polygon as a LineString. * * @param int $n * @return LineString|null */ public function interiorRingN(int $n): ?LineString { return $this->geometryN($n + 1); } /** * @return bool|null */ public function isSimple(): ?bool { if ($this->getGeos()) { // @codeCoverageIgnoreStart /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->isSimple(); // @codeCoverageIgnoreEnd } $segments = $this->explode(true); //TODO: instead of this O(n^2) algorithm implement Shamos-Hoey Algorithm which is only O(n*log(n)) foreach ($segments as $i => $segment) { foreach ($segments as $j => $checkSegment) { if ($i != $j) { if (Geometry::segmentIntersects($segment[0], $segment[1], $checkSegment[0], $checkSegment[1])) { return false; } } } } return true; } /** * For a given point, determine whether it's bounded by the given polygon. * Adapted from @source http://www.assemblysys.com/dataServices/php_pointinpolygon.php * * @see http://en.wikipedia.org/wiki/Point%5Fin%5Fpolygon * * @param Point $point * @param boolean $pointOnBoundary - whether a boundary should be considered "in" or not * @param boolean $pointOnVertex - whether a vertex should be considered "in" or not * * @return boolean */ public function pointInPolygon(Point $point, bool $pointOnBoundary = true, bool $pointOnVertex = true): bool { $vertices = $this->getPoints(); // Check if the point sits exactly on a vertex if ($this->pointOnVertex($point)) { return $pointOnVertex ? true : false; } // Check if the point is inside the polygon or on the boundary $intersections = 0; $verticesCount = count($vertices); for ($i = 1; $i < $verticesCount; $i++) { $vertex1 = $vertices[$i - 1]; $vertex2 = $vertices[$i]; if ( $vertex1->y() == $vertex2->y() && $vertex1->y() == $point->y() && $point->x() > min($vertex1->x(), $vertex2->x()) && $point->x() < max($vertex1->x(), $vertex2->x()) ) { // Check if point is on an horizontal polygon boundary return $pointOnBoundary ? true : false; } if ( $point->y() > min($vertex1->y(), $vertex2->y()) && $point->y() <= max($vertex1->y(), $vertex2->y()) && $point->x() <= max($vertex1->x(), $vertex2->x()) && $vertex1->y() != $vertex2->y() ) { $xinters = ($point->y() - $vertex1->y()) * ($vertex2->x() - $vertex1->x()) / ($vertex2->y() - $vertex1->y()) + $vertex1->x(); if ($xinters == $point->x()) { // Check if point is on the polygon boundary (other than horizontal) return $pointOnBoundary ? true : false; } if ($vertex1->x() == $vertex2->x() || $point->x() <= $xinters) { $intersections++; } } } // If the number of edges we passed through is even, then it's in the polygon. return $intersections % 2 !== 0; } /** * @param Point $point * @return bool */ public function pointOnVertex(Point $point): bool { foreach ($this->getPoints() as $vertex) { if ($point->equals($vertex)) { return true; } } return false; } /** * Checks whether the given geometry is spatially inside the Polygon * TODO: rewrite this. Currently supports point, linestring and polygon with only outer ring * @param Geometry $geometry * @return bool */ public function contains(Geometry $geometry): bool { if ($this->getGeos()) { // @codeCoverageIgnoreStart /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->contains($geometry->getGeos()); // @codeCoverageIgnoreEnd } $isInside = false; foreach ($geometry->getPoints() as $p) { if ($this->pointInPolygon($p)) { $isInside = true; // at least one point of the innerPoly is inside the outerPoly break; } } if (!$isInside) { return false; } if ($geometry->geometryType() === Geometry::POLYGON) { $geometry = $geometry->exteriorRing(); } elseif ($geometry->geometryType() !== Geometry::LINE_STRING) { return false; } foreach ($geometry->explode(true) as $innerEdge) { foreach ($this->exteriorRing()->explode(true) as $outerEdge) { if (Geometry::segmentIntersects($innerEdge[0], $innerEdge[1], $outerEdge[0], $outerEdge[1])) { return false; } } } return true; } /** * @return null|array{maxy: float, miny: float, maxx: float, minx: float} */ public function getBBox(): ?array { return $this->exteriorRing()->getBBox(); } /** * The boundary of a simple Surface is the set of closed Curves, * corresponding to its “exterior” and “interior” boundaries. * @return LineString|MultiLineString */ public function boundary(): ?Geometry { if ($this->isEmpty()) { return new LineString(); } if ($this->getGeos()) { // @codeCoverageIgnoreStart return geoPHP::geosToGeometry($this->getGeos()->boundary()); // @codeCoverageIgnoreEnd } $rings = $this->getComponents(); return $this->numInteriorRings() === 0 ? $rings[0] : new MultiLineString($rings); } } <?php declare(strict_types=1); namespace geoPHP\Geometry; use geoPHP\geoPHP; use geoPHP\Exception\InvalidGeometryException; /** * MultiLineString: A collection of LineStrings * * @method LineString[] getComponents() * @property LineString[] $components * * @phpstan-consistent-constructor */ class MultiLineString extends MultiCurve { /** * Checks and stores geometry components. * * @param LineString[] $components Array of LineString components. * * @throws InvalidGeometryException */ public function __construct(array $components = []) { parent::__construct($components, LineString::class); } /** * * Creates a MultiLineString from array of coordinates * * @param array{}|array<array{}|array<array<float|int|null>>> $coordinateArray * Multi-dimensional array of coordinates. * * @throws InvalidGeometryException * * @return MultiLineString */ public static function fromArray(array $coordinateArray): MultiLineString { $points = []; foreach ($coordinateArray as $point) { $points[] = LineString::fromArray($point); } return new static($points); } public function geometryType(): string { return Geometry::MULTI_LINE_STRING; } public function centroid(): Point { if ($this->isEmpty()) { return new Point(); } if ($this->getGeos()) { // @codeCoverageIgnoreStart return geoPHP::geosToGeometry($this->getGeos()->centroid()); // @codeCoverageIgnoreEnd } $x = 0; $y = 0; $totalLength = 0; $componentLength = 0; $components = $this->getComponents(); foreach ($components as $line) { if ($line->isEmpty()) { continue; } $componentCentroid = $line->getCentroidAndLength($componentLength); $x += $componentCentroid->x() * $componentLength; $y += $componentCentroid->y() * $componentLength; $totalLength += $componentLength; } return $totalLength === 0 ? $this->getPoints()[0] : new Point($x / $totalLength, $y / $totalLength); } } <?php declare(strict_types=1); namespace geoPHP\Geometry; use geoPHP\Exception\InvalidGeometryException; use geoPHP\geoPHP; use function abs; use const PHP_INT_MAX; /** * MultiGeometry is an abstract collection of geometries. * * @package geoPHP\Geometry */ abstract class MultiGeometry extends Collection { /** * Checks and stores geometry components. * * @param Geometry[] $components Array of Geometry components. * @param string $allowedComponentType A class the components must be instance of. Default: Geometry. * @param bool $allowEmptyComponents Allow creating geometries with empty components. Default: true. * * @throws InvalidGeometryException */ public function __construct( array $components = [], string $allowedComponentType = Geometry::class, bool $allowEmptyComponents = true ) { parent::__construct($components, $allowedComponentType, $allowEmptyComponents); } /** * A collection is simple if all it's components are simple. * * @return bool */ public function isSimple(): ?bool { if ($this->getGeos()) { // @codeCoverageIgnoreStart /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->isSimple(); // @codeCoverageIgnoreEnd } foreach ($this->components as $component) { if (!$component->isSimple()) { return false; } } return true; } /** * By default, the boundary of a collection is the boundary of it's components. * * @return Geometry */ public function boundary(): ?Geometry { if ($this->isEmpty()) { return new LineString(); } if ($this->getGeos()) { // @codeCoverageIgnoreStart /** @noinspection PhpUndefinedMethodInspection */ return geoPHP::geosToGeometry($this->getGeos()->boundary()); // @codeCoverageIgnoreEnd } $componentsBoundaries = []; foreach ($this->components as $component) { $componentsBoundaries[] = $component->boundary(); } return geoPHP::buildGeometry($componentsBoundaries); } public function area(): float { if ($this->getGeos()) { // @codeCoverageIgnoreStart /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->area(); // @codeCoverageIgnoreEnd } $area = 0.0; foreach ($this->components as $component) { $area += $component->area(); } return $area; } /** * Returns the length of this Collection in its associated spatial reference. * Eg. if Geometry is in geographical coordinate system it returns the length in degrees * @return float */ public function length(): float { $length = 0.0; foreach ($this->components as $component) { $length += $component->length(); } return $length; } public function length3D(): float { $length = 0.0; foreach ($this->components as $component) { $length += $component->length3D(); } return $length; } /** * Returns the degree based Geometry' length in meters. * * @param float $radius Default is the semi-major axis of WGS84. * @return float Length in meters. */ public function greatCircleLength(float $radius = geoPHP::EARTH_WGS84_SEMI_MAJOR_AXIS): float { $length = 0.0; foreach ($this->components as $component) { $length += $component->greatCircleLength($radius); } return $length; } public function haversineLength(): float { $length = 0.0; foreach ($this->components as $component) { $length += $component->haversineLength(); } return $length; } public function vincentyLength(): float { $length = 0.0; foreach ($this->components as $component) { $length += $component->vincentyLength(); } return $length; } public function minimumZ(): ?float { if (!$this->is3D()) { return null; } $min = PHP_INT_MAX; foreach ($this->components as $component) { $componentMin = $component->minimumZ(); if ($componentMin < $min) { $min = $componentMin; } } return $min < PHP_INT_MAX ? $min : null; } public function maximumZ(): ?float { if (!$this->is3D()) { return null; } $max = ~PHP_INT_MAX; foreach ($this->components as $component) { $componentMax = $component->maximumZ(); if ($componentMax > $max) { $max = $componentMax; } } return $max > ~PHP_INT_MAX ? $max : null; } public function zDifference(): ?float { if (!$this->is3D()) { return null; } $startPoint = $this->startPoint(); $endPoint = $this->endPoint(); if ($startPoint && $endPoint) { return (float) abs($startPoint->z() - $endPoint->z()); } else { return null; } } public function elevationGain(float $verticalTolerance = 0.0): float { $gain = 0.0; foreach ($this->components as $component) { $gain += $component->elevationGain($verticalTolerance); } return (float) $gain; } public function elevationLoss(float $verticalTolerance = 0.0): float { $loss = 0.0; foreach ($this->components as $component) { $loss += $component->elevationLoss($verticalTolerance); } return $loss; } public function minimumM(): ?float { if (!$this->isMeasured()) { return null; } $min = PHP_INT_MAX; foreach ($this->components as $component) { $componentMin = $component->minimumM(); if ($componentMin < $min) { $min = $componentMin; } } return $min < PHP_INT_MAX ? $min : null; } public function maximumM(): ?float { if (!$this->isMeasured()) { return null; } $max = ~PHP_INT_MAX; foreach ($this->components as $component) { $componentMax = $component->maximumM(); if ($componentMax > $max) { $max = $componentMax; } } return $max > ~PHP_INT_MAX ? $max : null; } public function startPoint(): ?Point { return null; } public function endPoint(): ?Point { return null; } public function isRing(): ?bool { return null; } public function isClosed(): ?bool { return null; } public function pointN(int $n): ?Point { return null; } public function exteriorRing(): ?LineString { return null; } public function numInteriorRings(): ?int { return null; } public function interiorRingN(int $n): ?LineString { return null; } } <?php declare(strict_types=1); namespace geoPHP\Geometry; use geoPHP\Exception\InvalidGeometryException; use geoPHP\geoPHP; use function abs; use function atan; use function atan2; use function cos; use function count; use function deg2rad; use function is_nan; use function sin; use function sqrt; use function tan; use const M_PI; use const PHP_INT_MAX; /** * A LineString is defined by a sequence of points, (X,Y) pairs, which define the reference points of the line string. * Linear interpolation between the reference points defines the resulting linestring. * * @phpstan-consistent-constructor */ class LineString extends Curve { /** * Checks and stores geometry components. * * @param Point[] $points Array of at least two Points with which to build the LineString. * * @throws InvalidGeometryException */ public function __construct(array $points = []) { parent::__construct($points); } /** * * Creates a LineString from array of coordinates * * @param array{}|array<array<float|int|null>> $coordinateArray Multi-dimensional array of coordinates. * * @throws InvalidGeometryException * * @return LineString */ public static function fromArray(array $coordinateArray): LineString { $points = []; foreach ($coordinateArray as $point) { $points[] = Point::fromArray($point); } return new static($points); } public function geometryType(): string { return Geometry::LINE_STRING; } /** * Returns the number of points of the LineString * * @return int */ public function numPoints(): int { return count($this->components); } /** * Returns the 1-based Nth point of the LineString. * Negative values are counted backwards from the end of the LineString. * * @param int $n Nth point of the LineString * @return Point|null */ public function pointN(int $n): ?Point { return $n >= 0 ? $this->geometryN($n) : $this->geometryN(count($this->components) - abs($n + 1)); } public function centroid(): Point { return $this->getCentroidAndLength(); } public function getCentroidAndLength(float &$length = 0.0): Point { if ($this->isEmpty()) { return new Point(); } if ($this->getGeos()) { // @codeCoverageIgnoreStart //return geoPHP::geosToGeometry($this->getGeos()->centroid()); // @codeCoverageIgnoreEnd } $x = 0; $y = 0; $length = 0.0; /** @var Point|null $previousPoint */ $previousPoint = null; foreach ($this->getPoints() as $point) { if ($previousPoint) { // Equivalent to $previousPoint->distance($point) but much faster $segmentLength = sqrt( ($previousPoint->x() - $point->x()) ** 2 + ($previousPoint->y() - $point->y()) ** 2 ); $length += $segmentLength; $x += ($previousPoint->x() + $point->x()) / 2 * $segmentLength; $y += ($previousPoint->y() + $point->y()) / 2 * $segmentLength; } $previousPoint = $point; } if ($length === 0.0) { return $this->startPoint(); } return new Point($x / $length, $y / $length); } /** * Returns the length of this Curve in its associated spatial reference. * Eg. if Geometry is in geographical coordinate system it returns the length in degrees * @return float */ public function length(): float { if ($this->getGeos()) { // @codeCoverageIgnoreStart /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->length(); // @codeCoverageIgnoreEnd } $length = 0.0; /** @var Point|null $previousPoint */ $previousPoint = null; foreach ($this->getComponents() as $point) { if ($previousPoint) { $length += sqrt( ($previousPoint->x() - $point->x()) ** 2 + ($previousPoint->y() - $point->y()) ** 2 ); } $previousPoint = $point; } return $length; } public function length3D(): float { $length = 0.0; /** @var Point|null $previousPoint */ $previousPoint = null; foreach ($this->getComponents() as $point) { if ($previousPoint) { $length += sqrt( ($previousPoint->x() - $point->x()) ** 2 + ($previousPoint->y() - $point->y()) ** 2 + ($previousPoint->z() - $point->z()) ** 2 ); } $previousPoint = $point; } return $length; } /** * @param float $radius Earth radius * @return float Length in meters */ public function greatCircleLength(float $radius = geoPHP::EARTH_WGS84_SEMI_MAJOR_AXIS): float { $length = 0.0; $rad = M_PI / 180; $points = $this->getPoints(); $numPoints = $this->numPoints() - 1; for ($i = 0; $i < $numPoints; ++$i) { // Simplified Vincenty formula with equal major and minor axes (a sphere) $lat1 = $points[$i]->y() * $rad; $lat2 = $points[$i + 1]->y() * $rad; $lon1 = $points[$i]->x() * $rad; $lon2 = $points[$i + 1]->x() * $rad; $deltaLon = $lon2 - $lon1; $cosLat1 = cos($lat1); $cosLat2 = cos($lat2); $sinLat1 = sin($lat1); $sinLat2 = sin($lat2); $cosDeltaLon = cos($deltaLon); $d = $radius * atan2( sqrt( ($cosLat2 * sin($deltaLon)) ** 2 + ($cosLat1 * $sinLat2 - $sinLat1 * $cosLat2 * $cosDeltaLon) ** 2 ), $sinLat1 * $sinLat2 + $cosLat1 * $cosLat2 * $cosDeltaLon ); if ($points[$i]->is3D()) { $d = sqrt( $d ** 2 + ($points[$i + 1]->z() - $points[$i]->z()) ** 2 ); } $length += $d; } // Returns length in meters. return $length; } /** * @return float Haversine length of geometry in degrees */ public function haversineLength(): float { $distance = 0.0; $points = $this->getPoints(); $numPoints = $this->numPoints() - 1; for ($i = 0; $i < $numPoints; ++$i) { $point = $points[$i]; $nextPoint = $points[$i + 1]; $degree = (geoPHP::EARTH_WGS84_SEMI_MAJOR_AXIS * acos( sin(deg2rad($point->y())) * sin(deg2rad($nextPoint->y())) + cos(deg2rad($point->y())) * cos(deg2rad($nextPoint->y())) * cos(deg2rad(abs($point->x() - $nextPoint->x()))) ) ); if (!is_nan($degree)) { $distance += $degree; } } return $distance; } /** * @source https://github.com/mjaschen/phpgeo/blob/master/src/Location/Distance/Vincenty.php * @author Marcus Jaschen <mjaschen@gmail.com> * @license https://opensource.org/licenses/GPL-3.0 GPL * (note: geoPHP uses "GPL version 2 (or later)" license which is compatible with GPLv3) * * @return float Length in meters */ public function vincentyLength(): float { $length = 0.0; $rad = M_PI / 180; $points = $this->getPoints(); $numPoints = $this->numPoints() - 1; for ($i = 0; $i < $numPoints; ++$i) { // Inverse Vincenty formula $lat1 = $points[$i]->y() * $rad; $lat2 = $points[$i + 1]->y() * $rad; $lng1 = $points[$i]->x() * $rad; $lng2 = $points[$i + 1]->x() * $rad; $semiMajor = geoPHP::EARTH_WGS84_SEMI_MAJOR_AXIS; $semiMinor = geoPHP::EARTH_WGS84_SEMI_MINOR_AXIS; $f = 1 / geoPHP::EARTH_WGS84_FLATTENING; $deltaL = $lng2 - $lng1; $u1 = atan((1 - $f) * tan($lat1)); $u2 = atan((1 - $f) * tan($lat2)); $iterationLimit = 100; $lambda = $deltaL; $sinU1 = sin($u1); $sinU2 = sin($u2); $cosU1 = cos($u1); $cosU2 = cos($u2); do { $sinLambda = sin($lambda); $cosLambda = cos($lambda); $sinSigma = sqrt( ($cosU2 * $sinLambda) * ($cosU2 * $sinLambda) + ($cosU1 * $sinU2 - $sinU1 * $cosU2 * $cosLambda) * ($cosU1 * $sinU2 - $sinU1 * $cosU2 * $cosLambda) ); if ($sinSigma == 0) { return 0.0; } $cosSigma = $sinU1 * $sinU2 + $cosU1 * $cosU2 * $cosLambda; $sigma = atan2($sinSigma, $cosSigma); $sinAlpha = $cosU1 * $cosU2 * $sinLambda / $sinSigma; $cosSqAlpha = 1 - $sinAlpha * $sinAlpha; $cos2SigmaM = 0; if ($cosSqAlpha <> 0) { $cos2SigmaM = $cosSigma - 2 * $sinU1 * $sinU2 / $cosSqAlpha; } $c = $f / 16 * $cosSqAlpha * (4 + $f * (4 - 3 * $cosSqAlpha)); $lambdaP = $lambda; $lambda = $deltaL + (1 - $c) * $f * $sinAlpha * ($sigma + $c * $sinSigma * ($cos2SigmaM + $c * $cosSigma * (- 1 + 2 * $cos2SigmaM * $cos2SigmaM))); } while (abs($lambda - $lambdaP) > 1e-12 && --$iterationLimit > 0); if ($iterationLimit == 0) { throw new \RuntimeException('Vincenty distance calculation not converging.'); } $uSq = $cosSqAlpha * ($semiMajor * $semiMajor - $semiMinor * $semiMinor) / ($semiMinor * $semiMinor); $a = 1 + $uSq / 16384 * (4096 + $uSq * (- 768 + $uSq * (320 - 175 * $uSq))); $b = $uSq / 1024 * (256 + $uSq * (- 128 + $uSq * (74 - 47 * $uSq))); $deltaSigma = $b * $sinSigma * ($cos2SigmaM + $b / 4 * ($cosSigma * (-1 + 2 * $cos2SigmaM * $cos2SigmaM) - $b / 6 * $cos2SigmaM * (-3 + 4 * $sinSigma * $sinSigma) * (-3 + 4 * $cos2SigmaM * $cos2SigmaM))); $length += $semiMinor * $a * ($sigma - $deltaSigma); } // Returns length in meters. return $length; } public function minimumZ(): ?float { if (!$this->is3D()) { return null; } $min = PHP_INT_MAX; foreach ($this->getPoints() as $point) { if ($point->z() < $min) { $min = $point->z(); } } return $min < PHP_INT_MAX ? $min : null; } public function maximumZ(): ?float { if (!$this->is3D()) { return null; } $max = ~PHP_INT_MAX; foreach ($this->getPoints() as $point) { if ($point->z() > $max) { $max = $point->z(); } } return $max > ~PHP_INT_MAX ? $max : null; } public function zDifference(): ?float { if (!$this->is3D()) { return null; } return abs($this->startPoint()->z() - $this->endPoint()->z()); } /** * Returns the cumulative elevation gain of the LineString * * @param float $verticalTolerance Smoothing factor filtering noisy elevation data. * Its unit equals to the z-coordinates unit (meters for geographical coordinates) * If the elevation data comes from a DEM, a value around 3.5 can be acceptable. * * @return float */ public function elevationGain(float $verticalTolerance = 0.0): float { if (!$this->is3D()) { return 0.0; } $gain = 0.0; $lastEle = $this->startPoint()->z(); $pointCount = $this->numPoints(); foreach ($this->getPoints() as $i => $point) { if (abs($point->z() - $lastEle) > $verticalTolerance || $i === $pointCount - 1) { if ($point->z() > $lastEle) { $gain += $point->z() - $lastEle; } $lastEle = $point->z(); } } return $gain; } /** * Returns the cumulative elevation loss of the LineString * * @param float $verticalTolerance Smoothing factor filtering noisy elevation data. * Its unit equals to the z-coordinates unit (meters for geographical coordinates) * If the elevation data comes from a DEM, a value around 3.5 can be acceptable. * * @return float */ public function elevationLoss(float $verticalTolerance = 0.0): float { if (!$this->is3D()) { return 0.0; } $loss = 0.0; $lastEle = $this->startPoint()->z(); $pointCount = $this->numPoints(); foreach ($this->getPoints() as $i => $point) { if (abs($point->z() - $lastEle) > $verticalTolerance || $i === $pointCount - 1) { if ($point->z() < $lastEle) { $loss += $lastEle - $point->z(); } $lastEle = $point->z(); } } return $loss; } public function minimumM(): ?float { if (!$this->isMeasured()) { return null; } $min = PHP_INT_MAX; foreach ($this->getPoints() as $point) { if ($point->m() < $min) { $min = $point->m(); } } return $min < PHP_INT_MAX ? $min : null; } public function maximumM(): ?float { if (!$this->isMeasured()) { return null; } $max = ~PHP_INT_MAX; foreach ($this->getPoints() as $point) { if ($point->m() > $max) { $max = $point->m(); } } return $max > ~PHP_INT_MAX ? $max : null; } /** * Get all line segments. By default returns segments as array of LineStrings of two points. * * @param bool $toArray Return segments as arrays of Point pairs. * * @return LineString[]|array<array{Point, Point}> */ public function explode(bool $toArray = false): ?array { $points = $this->getPoints(); $numPoints = count($points); if ($numPoints < 2) { return []; } $parts = []; for ($i = 1; $i < $numPoints; ++$i) { $segment = [$points[$i - 1], $points[$i]]; $parts[] = $toArray ? $segment : new LineString($segment); } return $parts; } /** * Checks that LineString is a Simple Geometry. * * A Curve is simple if it does not pass through the same Point twice * with the possible exception of the two end points. * * WARNING: Current implementation has known problems with self tangency. * * @return boolean */ public function isSimple(): ?bool { if ($this->getGeos()) { // @codeCoverageIgnoreStart /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->isSimple(); // @codeCoverageIgnoreEnd } // As of OGC specification a ring is simple only if its start and end points equals in all coordinates // Neither GEOS, nor PostGIS support it // if ($this->is3D() // && $this->startPoint()->equals($this->endPoint()) // && $this->startPoint()->z() !== $this->endPoint()->z() // ) { // return false; // } $segments = $this->explode(true); foreach ($segments as $i => $segment) { foreach ($segments as $j => $checkSegment) { if ( $i != $j && Geometry::segmentIntersects($segment[0], $segment[1], $checkSegment[0], $checkSegment[1]) ) { return false; } } } return true; } /** * @param LineString $segment * @return bool */ public function lineSegmentIntersect(LineString $segment): bool { return Geometry::segmentIntersects( $this->startPoint(), $this->endPoint(), $segment->startPoint(), $segment->endPoint() ); } /** * @param Geometry|Collection $geometry * @return float|null */ public function distance(Geometry $geometry): ?float { if ($this->getGeos()) { // @codeCoverageIgnoreStart /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->distance($geometry->getGeos()); // @codeCoverageIgnoreEnd } if ($geometry->geometryType() === Geometry::POINT) { // This is defined in the Point class nicely return $geometry->distance($this); } if ($geometry->geometryType() === Geometry::LINE_STRING) { $distance = null; $geometrySegments = $geometry->explode(); foreach ($this->explode() as $seg1) { /** @var LineString $seg2 */ foreach ($geometrySegments as $seg2) { if ($seg1->lineSegmentIntersect($seg2)) { return 0.0; } // Because line-segments are straight, the shortest distance will be at one of the endpoints. // If they are parallel endpoint calculation is still accurate. $checkDistance1 = $seg1->startPoint()->distance($seg2); $checkDistance2 = $seg1->endPoint()->distance($seg2); $checkDistance3 = $seg2->startPoint()->distance($seg1); $checkDistance4 = $seg2->endPoint()->distance($seg1); $checkDistance = min($checkDistance1, $checkDistance2, $checkDistance3, $checkDistance4); if ($checkDistance === 0.0) { return 0.0; } if ($distance === null || $checkDistance < $distance) { $distance = $checkDistance; } } } return $distance; } else { // It can be treated as collection return parent::distance($geometry); } } } <?php declare(strict_types=1); namespace geoPHP\Geometry; use geoPHP\geoPHP; use geoPHP\Exception\InvalidGeometryException; use function array_merge; /** * MultiPolygon: A collection of Polygons * * @method Polygon[] getComponents() * @property Polygon[] $components * * @phpstan-consistent-constructor */ class MultiPolygon extends MultiSurface { /** * Checks and stores geometry components. * * @param Polygon[] $components Array of Polygon components. * * @throws InvalidGeometryException */ public function __construct(array $components = []) { parent::__construct($components, Polygon::class); } /** * * Creates a MultiPolygon from array of coordinates * * @param array{}|array<array{}|array<array<array<float|int|null>>>> $coordinateArray * Multi-dimensional array of coordinates. * * @throws InvalidGeometryException * * @return MultiPolygon */ public static function fromArray(array $coordinateArray): MultiPolygon { $points = []; foreach ($coordinateArray as $point) { $points[] = Polygon::fromArray($point); } return new static($points); } public function geometryType(): string { return Geometry::MULTI_POLYGON; } public function centroid(): Point { if ($this->isEmpty()) { return new Point(); } if ($this->getGeos()) { // @codeCoverageIgnoreStart return geoPHP::geosToGeometry($this->getGeos()->centroid()); // @codeCoverageIgnoreEnd } $x = 0; $y = 0; $totalArea = 0; foreach ($this->getComponents() as $component) { if ($component->isEmpty()) { continue; } $componentArea = $component->area(); $totalArea += $componentArea; $componentCentroid = $component->centroid(); $x += $componentCentroid->x() * $componentArea; $y += $componentCentroid->y() * $componentArea; } return new Point($x / $totalArea, $y / $totalArea); } public function area(): float { if ($this->getGeos()) { // @codeCoverageIgnoreStart /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->area(); // @codeCoverageIgnoreEnd } $area = 0; foreach ($this->components as $component) { $area += $component->area(); } return $area; } public function boundary(): ?Geometry { $rings = []; foreach ($this->getComponents() as $component) { $rings = array_merge($rings, $component->components); } return new MultiLineString($rings); } } <?php declare(strict_types=1); namespace geoPHP\Geometry; use geoPHP\Exception\InvalidGeometryException; use geoPHP\geoPHP; use function count; use function gettype; use function get_class; /** * Collection: Abstract class for compound geometries * * A geometry is a collection if it is made up of other * component geometries. Therefore everything but a Point * is a Collection. For example a LingString is a collection * of Points. A Polygon is a collection of LineStrings etc. */ abstract class Collection extends Geometry { /** * @var Geometry[]|Collection[] */ protected $components = []; /** * @var bool True if Geometry has Z (altitude) value */ protected $hasZ = false; /** * @var bool True if Geometry has M (measure) value */ protected $isMeasured = false; /** * Checks and stores geometry components. * * @param Geometry[] $components Array of geometries. * @param bool $allowEmptyComponents Allow creating geometries with empty components. Default: false. * @param string $allowedComponentType A class the components must be instance of. Default: any Geometry. * * @throws InvalidGeometryException */ public function __construct( array $components = [], string $allowedComponentType = Geometry::class, bool $allowEmptyComponents = false ) { /** @var Geometry[] $components */ foreach ($components as $i => $component) { if ($component instanceof $allowedComponentType) { if (!$allowEmptyComponents && $component->isEmpty()) { throw new InvalidGeometryException( 'Cannot create a collection of empty ' . $component->geometryType() . 's (' . ($i + 1) . '. component)' ); } if ($component->is3D() && !$this->hasZ) { $this->hasZ = true; } if ($component->isMeasured() && !$this->isMeasured) { $this->isMeasured = true; } } else { $componentType = gettype($component) !== 'object' ? gettype($component) : get_class($component); throw new InvalidGeometryException( "Cannot construct " . static::class . '. ' . "Expected $allowedComponentType components, got $componentType." ); } } $this->components = $components; } /** * Returns TRUE if this geometric object has z coordinate values. * * @return bool */ public function is3D(): bool { return $this->hasZ; } /** * Returns TRUE if this geometric object has m coordinate values. * * @return bool */ public function isMeasured(): bool { return $this->isMeasured; } /** * Get all sub-geometry components of the geometry. * * @return Geometry[] */ public function getComponents(): array { return $this->components; } /** * Swaps X and Y coordinates of the geometry. * * Useful to fix geometries with lat-lng coordinate order. * * @return self */ public function invertXY(): self { foreach ($this->components as $component) { $component->invertXY(); } $this->flushGeosCache(); return $this; } /** * Removes 3D information and measures from the geometry. * * @return void */ public function flatten(): void { if ($this->is3D() || $this->isMeasured()) { foreach ($this->components as $component) { $component->flatten(); } $this->hasZ = false; $this->isMeasured = false; $this->flushGeosCache(); } } /** * The minimum bounding box of the Geometry as array. * * @see envelope() * * @return null|array{maxy: float, miny: float, maxx: float, minx: float} * Array of min and max values of x and y coordinates. */ public function getBBox(): ?array { if ($this->isEmpty()) { return null; } if ($this->getGeos()) { // @codeCoverageIgnoreStart /** @noinspection PhpUndefinedMethodInspection */ $envelope = $this->getGeos()->envelope(); /** @noinspection PhpUndefinedMethodInspection */ if ($envelope->typeName() == 'Point') { return geoPHP::geosToGeometry($envelope)->getBBox(); } /** @noinspection PhpUndefinedMethodInspection */ $geosRing = $envelope->exteriorRing(); /** @noinspection PhpUndefinedMethodInspection */ return [ 'maxy' => $geosRing->pointN(3)->getY(), 'miny' => $geosRing->pointN(1)->getY(), 'maxx' => $geosRing->pointN(1)->getX(), 'minx' => $geosRing->pointN(3)->getX(), ]; // @codeCoverageIgnoreEnd } // Go through each component and get the max and min x and y $maxX = $maxY = $minX = $minY = 0.0; foreach ($this->components as $i => $component) { $componentBoundingBox = $component->getBBox(); if ($componentBoundingBox === null) { continue; } // On the first run through, set the bounding box to the component's bounding box if ($i == 0) { $maxX = $componentBoundingBox['maxx']; $maxY = $componentBoundingBox['maxy']; $minX = $componentBoundingBox['minx']; $minY = $componentBoundingBox['miny']; } // Do a check and replace on each boundary, slowly growing the bounding box $maxX = $componentBoundingBox['maxx'] > $maxX ? $componentBoundingBox['maxx'] : $maxX; $maxY = $componentBoundingBox['maxy'] > $maxY ? $componentBoundingBox['maxy'] : $maxY; $minX = $componentBoundingBox['minx'] < $minX ? $componentBoundingBox['minx'] : $minX; $minY = $componentBoundingBox['miny'] < $minY ? $componentBoundingBox['miny'] : $minY; } return [ 'maxy' => $maxY, 'miny' => $minY, 'maxx' => $maxX, 'minx' => $minX, ]; } /** * Returns every sub-geometry as a multidimensional array. * * @return array<mixed> */ public function asArray(): array { $array = []; foreach ($this->components as $component) { $array[] = $component->asArray(); } return $array; } /** * The number of component geometries in the collection. * * @return int|null */ public function numGeometries(): ?int { return count($this->components); } /** * Returns the geometry N. in the collection. Note that the index starts at 1. * * @param int $n 1-based index. * * @return Geometry|null The geometry, or null if not found. */ public function geometryN(int $n): ?Geometry { return isset($this->components[$n - 1]) ? $this->components[$n - 1] : null; } /** * Returns true if the geometric object is the empty Geometry. * * A collection is not empty if it has at least one non empty component. * * @return bool If true, then the geometric object represents the empty point set ∅ for the coordinate space. */ public function isEmpty(): bool { foreach ($this->components as $component) { if (!$component->isEmpty()) { return false; } } return true; } /** * The number of Points in the Geometry. * * @return int */ public function numPoints(): int { $num = 0; foreach ($this->components as $component) { $num += $component->numPoints(); } return $num; } /** * Get all the points of the geometry. * * @return Point[] */ public function getPoints(): array { $points = []; foreach ($this->getComponents() as $component) { if ($component instanceof Point) { $points[] = $component; } else { foreach ($component->getPoints() as $componentPoint) { $points[] = $componentPoint; } } } return $points; } /** * Returns TRUE if this geometry is "spatially equal" to other geometry. * * @param Geometry $geometry * * @return bool */ public function equals(Geometry $geometry): bool { if ($this->getGeos()) { // @codeCoverageIgnoreStart /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->equals($geometry->getGeos()); // @codeCoverageIgnoreEnd } // To test for equality we check to make sure that there is a matching point // in the other geometry for every point in this geometry. // This is slightly more strict than the standard, which // uses Within(A,B) = true and Within(B,A) = true // @@TODO: Eventually we could fix this by using some sort of simplification // method that strips redundant vertices (that are all in a row) $thisPoints = $this->getPoints(); $otherPoints = $geometry->getPoints(); // First do a check to make sure they have the same number of vertices if (count($thisPoints) != count($otherPoints)) { return false; } foreach ($thisPoints as $point) { $foundMatch = false; foreach ($otherPoints as $key => $testPoint) { if ($point->equals($testPoint)) { $foundMatch = true; unset($otherPoints[$key]); break; } } if (!$foundMatch) { return false; } } // All points match, return TRUE return true; } /** * Get all line segments. * * @param bool $toArray Return segments as LineString or array of start and end points. Explode(true) is faster. * * @return LineString[] Returns line segments. */ public function explode(bool $toArray = false): ?array { $parts = []; foreach ($this->components as $component) { foreach ($component->explode($toArray) as $part) { $parts[] = $part; } } return $parts; } /** * The distance of this geometry to another geometry in their associated spatial reference. * * @return float|null */ public function distance(Geometry $geometry): ?float { if ($this->getGeos()) { // @codeCoverageIgnoreStart /** @noinspection PhpUndefinedMethodInspection */ return $this->getGeos()->distance($geometry->getGeos()); // @codeCoverageIgnoreEnd } $distance = null; foreach ($this->components as $component) { $componentDistance = $component->distance($geometry); // Stop if distance of a component is zero. if ($componentDistance === 0.0) { return 0.0; } // Distance to/from an empty geometry is null. Just skip this geometry. if ($componentDistance === null) { continue; } if ($distance === null || $componentDistance < $distance) { $distance = $componentDistance; } } return $distance; } // Not valid for this geometry type // -------------------------------- public function x(): ?float { return null; } public function y(): ?float { return null; } public function z(): ?float { return null; } public function m(): ?float { return null; } } <?php declare(strict_types=1); namespace geoPHP\Geometry; use geoPHP\Exception\InvalidGeometryException; /** * Class MultiSurface * TODO write this * * @package geoPHP\Geometry */ abstract class MultiSurface extends MultiGeometry { /** * Checks and stores geometry components. * * @param Surface[] $components Array of Surface components. * @param string $allowedComponentType A class the components must be instance of. Default: Curve. * * @throws InvalidGeometryException */ public function __construct( array $components = [], string $allowedComponentType = Surface::class ) { parent::__construct($components, $allowedComponentType); } public function geometryType(): string { return Geometry::MULTI_SURFACE; } public function dimension(): int { return 2; } } <?php declare(strict_types=1); namespace geoPHP\Geometry; use geoPHP\Exception\Exception; use geoPHP\Exception\InvalidGeometryException; use geoPHP\geoPHP; use function array_merge; /** * GeometryCollection: A heterogeneous collection of geometries */ class GeometryCollection extends MultiGeometry { /** * Checks and stores geometry components. * * @param Geometry[] $components Array of geometries. Components of GeometryCollection can be * any of valid Geometry types, including empty geometry. * * @throws InvalidGeometryException */ public function __construct(array $components = []) { parent::__construct($components, Geometry::class); } public function geometryType(): string { return Geometry::GEOMETRY_COLLECTION; } /** * @return int Returns the highest spatial dimension of components */ public function dimension(): int { $dimension = 0; foreach ($this->getComponents() as $component) { if ($component->dimension() > $dimension) { $dimension = $component->dimension(); } } return $dimension; } /** * Not valid for this geometry type * @return null */ public function isSimple(): ?bool { return null; } /** * In a GeometryCollection, the centroid is equal to the centroid of * the set of component Geometries of highest dimension * (since the lower-dimension geometries contribute zero "weight" to the centroid). * * @return Point * @throws Exception */ public function centroid(): Point { if ($this->isEmpty()) { return new Point(); } if ($this->getGeos()) { // @codeCoverageIgnoreStart return geoPHP::geosToGeometry($this->getGeos()->centroid()); // @codeCoverageIgnoreEnd } $geometries = $this->explodeGeometries(); $highestDimension = 0; foreach ($geometries as $geometry) { if ($geometry->dimension() > $highestDimension) { $highestDimension = $geometry->dimension(); } if ($highestDimension === 2) { break; } } $highestDimensionGeometries = []; foreach ($geometries as $geometry) { if ($geometry->dimension() === $highestDimension) { $highestDimensionGeometries[] = $geometry; } } $reducedGeometry = geoPHP::geometryReduce($highestDimensionGeometries); if ($reducedGeometry->geometryType() === Geometry::GEOMETRY_COLLECTION) { throw new \Exception('Internal error: GeometryCollection->centroid() calculation failed.'); } return $reducedGeometry->centroid(); } /** * Returns every sub-geometry as a multidimensional array * * Because geometryCollections are heterogeneous we need to specify which type of geometries they contain. * We need to do this because, for example, there would be no way to tell the difference between a * MultiPoint or a LineString, since they share the same structure (collection * of points). So we need to call out the type explicitly. * * @return array<array{type: string, components: array<mixed>}> */ public function asArray(): array { $array = []; foreach ($this->getComponents() as $component) { $array[] = [ 'type' => $component->geometryType(), 'components' => $component->asArray(), ]; } return $array; } /** * @return Geometry[] */ public function explodeGeometries(): array { $geometries = []; foreach ($this->components as $component) { if ($component instanceof GeometryCollection) { foreach ($component->explodeGeometries() as $subComponent) { $geometries[] = $subComponent; } } else { $geometries[] = $component; } } return $geometries; } // Not valid for this geometry public function boundary(): ?Geometry { return null; } } <?php declare(strict_types=1); namespace geoPHP\Geometry; use geoPHP\Exception\InvalidGeometryException; /** * Class Curve * TODO write this * * @package geoPHP\Geometry * * @property Point[] $components A curve consists of sequence of Points * @method Point[] getComponents() * @method Point|null geometryN(int $n) */ abstract class Curve extends Collection { /** * Checks and stores geometry components. * * @param Point[] $components Array of Point components. * * @throws InvalidGeometryException */ public function __construct(array $components = []) { if (is_array($components) && count($components) == 1) { throw new InvalidGeometryException("Cannot construct a " . static::class . " with a single point"); } parent::__construct($components, Point::class, false); } /** * @var Point|null */ protected $startPoint = null; /** * @var Point|null */ protected $endPoint = null; /** * Returns the name of the instantiable subtype of Geometry of which the geometric object is an instantiable member. * * @return string */ public function geometryType(): string { return Geometry::CURVE; } /** * The inherent dimension of the geometric object, which must be less than or equal to the coordinate dimension. * In non-homogeneous collections, this will return the largest topological dimension of the contained objects. * * @return int */ public function dimension(): int { return 1; } /** * Returns true if the Curve represents the empty set. * * A Curve is empty if and only if it has no components. * * @return bool */ public function isEmpty(): bool { return count($this->components) === 0; } /** * The boundary of a non-closed Curve consists of its two end Points. * End point are represented as a MultiPoint geometry. * * @see OGC SFA 6.1.6.1 * * @return MultiPoint */ public function boundary(): ?Geometry { return $this->isEmpty() || $this->isClosed() ? new MultiPoint() : new MultiPoint([$this->startPoint(), $this->endPoint()]); } public function startPoint(): ?Point { if (!isset($this->startPoint)) { $this->startPoint = $this->components[0] ?? null; } return $this->startPoint; } public function endPoint(): ?Point { if (!isset($this->endPoint)) { $this->endPoint = $this->components[count($this->components) - 1] ?? null; } return $this->endPoint; } /** * A Curve is closed if its start Point is equal to its end Point. * * @see OGC SFA 6.1.6.1 * * @return boolean */ public function isClosed(): bool { return !$this->isEmpty() ? $this->startPoint()->equals($this->endPoint()) : false; } public function isRing(): bool { return ($this->isClosed() && $this->isSimple()); } /** * @return Point[] */ public function getPoints(): array { return $this->getComponents(); } // Not valid for this geometry type // -------------------------------- public function area(): float { return 0.0; } public function exteriorRing(): ?LineString { return null; } public function numInteriorRings(): ?int { return null; } public function interiorRingN(int $n): ?LineString { return null; } } <?php /** * This file contains the BinaryReader class. * For more information see the class description below. * * @author Peter Bathory <peter.bathory@cartographia.hu> * @since 2016-02-18 * * This code is open-source and licenced under the Modified BSD License. * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace geoPHP\Adapter; /** * Helper class BinaryWriter. * * A simple binary writer supporting both byte orders. */ class BinaryWriter { const BIG_ENDIAN = 0; const LITTLE_ENDIAN = 1; private $endianness = 0; /** * @param integer $endianness Constant value self::BIG_ENDIAN or self::LITTLE_ENDIAN. */ public function __construct(int $endianness = 0) { $this->endianness = $endianness === self::BIG_ENDIAN ? self::BIG_ENDIAN : self::LITTLE_ENDIAN; } /** * @return bool Returns true if Writer is in BigEndian mode */ public function isBigEndian(): bool { return $this->endianness === self::BIG_ENDIAN; } /** * @return bool Returns true if Writer is in LittleEndian mode */ public function isLittleEndian(): bool { return $this->endianness === self::LITTLE_ENDIAN; } /** * Writes a signed 8-bit integer * @param int|float $value * @return string The integer as a binary string */ public function writeSInt8($value): string { return pack('c', (int) $value); } /** * Writes an unsigned 8-bit integer * @param int|float $value * @return string The integer as a binary string */ public function writeUInt8($value): string { return pack('C', (int) $value); } /** * Writes an unsigned 32-bit integer * @param int|float $value * @return string The integer as a binary string */ public function writeUInt32($value): string { return pack($this->isLittleEndian() ? 'V' : 'N', (int) $value); } /** * Writes a double * @param float $value * @return string The floating point number as a binary string */ public function writeDouble($value): string { return $this->isLittleEndian() ? pack('d', (float) $value) : strrev(pack('d', (float) $value)); } /** * Writes a positive integer as an unsigned base-128 varint * * Ported from https://github.com/cschwarz/wkx/blob/master/lib/binaryreader.js * * @param int|float $value * @return string The integer as a binary string */ public function writeUVarInt($value): string { $value = (int) $value; $out = ''; while (($value & 0xFFFFFF80) !== 0) { $out .= $this->writeUInt8(($value & 0x7F) | 0x80); // Zero fill by 7 zero if ($value >= 0) { $value >>= 7; } else { $value = ((~$value) >> 7) ^ (0x7fffffff >> (7 - 1)); } } $out .= $this->writeUInt8($value & 0x7F); return $out; } /** * Writes an integer as a signed base-128 varint * @param int|float $value * @return string The integer as a binary string */ public function writeSVarInt($value): string { return $this->writeUVarInt(self::zigZagEncode((int) $value)); } /** * ZigZag encoding maps signed integers to unsigned integers * * @param int $value Signed integer * @return int Encoded positive integer value */ public static function zigZagEncode($value) { return ($value << 1) ^ ($value >> 31); } } <?php namespace geoPHP\Adapter; use geoPHP\Geometry\Geometry; /** * EWKB (Extended Well Known Binary) Adapter */ class EWKB extends WKB { public function write(Geometry $geometry, bool $writeAsHex = false, bool $bigEndian = false): string { $this->SRID = $geometry->getSRID(); $this->hasSRID = $this->SRID !== null; return parent::write($geometry, $writeAsHex, $bigEndian); } protected function writeType(Geometry $geometry, bool $writeSRID = false): string { return parent::writeType($geometry, true); } } <?php namespace geoPHP\Adapter; use geoPHP\Exception\IOException; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\GeometryCollection; use geoPHP\Geometry\Point; use geoPHP\Geometry\MultiPoint; use geoPHP\Geometry\LineString; use geoPHP\Geometry\Polygon; use geoPHP\Geometry\MultiPolygon; /* * (c) Camptocamp <info@camptocamp.com> * (c) Patrick Hayes * * This code is open-source and licenced under the Modified BSD License. * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * PHP Google Geocoder Adapter * * * @package geoPHP * @author Patrick Hayes <patrick.d.hayes@gmail.com> */ class GoogleGeocode implements GeoAdapter { /** @var \stdClass $result */ protected $result; /** * Makes a geocoding (lat/lon lookup) with an address string or array geometry objects. * Detailed documentation of response values can be found in: * * @see https://developers.google.com/maps/documentation/geocoding/requests-geocoding * * @param string $address Address to geocode. * @param string $apiKey Your application's Google Maps Geocoding API key. * @param string $returnType Type of Geometry to return. * Can either be 'points' or 'bounds' (polygon). * @param string[]|Geometry|null $bounds Limit the search area to within this region. * For example by default geocoding "Cairo" will return the location of Cairo Egypt. * If you pass a polygon of Illinois, it will return Cairo IL. * @param boolean $returnMultiple Return all results in a multipoint or multipolygon. * * @return Geometry * @throws IOException If geocoding fails. */ public function read( string $address, string $apiKey = null, string $returnType = 'point', $bounds = null, bool $returnMultiple = false ): Geometry { if ($bounds instanceof Geometry) { $bounds = $bounds->getBBox(); } if (is_array($bounds)) { $boundsString = '&bounds=' . $bounds['miny'] . ',' . $bounds['minx'] . '|' . $bounds['maxy'] . ',' . $bounds['maxx']; } else { $boundsString = ''; } $url = "http://maps.googleapis.com/maps/api/geocode/json"; $url .= '?address=' . urlencode($address); $url .= $boundsString . ($apiKey ? '&key=' . $apiKey : ''); $this->result = json_decode(@file_get_contents($url)); if ($this->result->status == 'OK') { if (!$returnMultiple) { if ($returnType == 'point') { return $this->getPoint(); } if ($returnType == 'bounds' || $returnType == 'polygon') { return $this->getPolygon(); } } else { if ($returnType == 'point') { $points = []; foreach ($this->result->results as $delta => $item) { $points[] = $this->getPoint($delta); } return new MultiPoint($points); } if ($returnType == 'bounds' || $returnType == 'polygon') { $polygons = []; foreach ($this->result->results as $delta => $item) { $polygons[] = $this->getPolygon($delta); } return new MultiPolygon($polygons); } } } elseif ($this->result->status == 'ZERO_RESULTS') { return new GeometryCollection(); } else { if ($this->result->status) { throw new IOException( 'Error in Google Reverse Geocoder: ' . $this->result->status . (isset($this->result->error_message) ? '. ' . $this->result->error_message : '') ); } else { throw new IOException('Unknown error in Google Reverse Geocoder'); } } return new GeometryCollection(); } /** * Makes a Reverse Geocoding (address lookup) with the (center) point of Geometry. * Detailed documentation of response values can be found in: * * @see https://developers.google.com/maps/documentation/geocoding/requests-reverse-geocoding * * @param Geometry $geometry * @param string $apiKey Your application's Google Maps Geocoding API key. * @param string $returnType Should be either 'string' or 'array' or 'full'. * @param string $language The language in which to return results. * If not set, geocoder tries to use the native language of the domain. * * @return string A formatted address. * @throws IOException If geocoding fails */ public function write( Geometry $geometry, ?string $apiKey = null, ?string $returnType = 'string', ?string $language = null ): string { return $this->reverseGeocode($geometry, $apiKey, 'string', $language); } /** * Makes a Reverse Geocoding (address lookup) with the (center) point of Geometry. * Detailed documentation of response values can be found in: * * @see https://developers.google.com/maps/documentation/geocoding/requests-reverse-geocoding * * @param Geometry $geometry * @param string $apiKey Your application's Google Maps Geocoding API key. * @param string $language The language in which to return results. * If not set, geocoder tries to use the native language of the domain. * * @return string[] Array of address components. * @throws IOException If geocoding fails */ public function writeAsArray( Geometry $geometry, ?string $apiKey = null, ?string $language = null ): array { return $this->reverseGeocode($geometry, $apiKey, 'array', $language); } /** * Makes a Reverse Geocoding (address lookup) with the (center) point of Geometry. * Detailed documentation of response values can be found in: * * @see https://developers.google.com/maps/documentation/geocoding/requests-reverse-geocoding * * @param Geometry $geometry * @param string $apiKey Your application's Google Maps Geocoding API key. * @param string $returnType Should be either 'string' or 'array' or 'full' * @param string $language The language in which to return results. * If not set, geocoder tries to use the native language of the domain. * * @return string|string[] A formatted address or array of address components. * @throws IOException If geocoding fails */ protected function reverseGeocode( Geometry $geometry, ?string $apiKey = null, ?string $returnType = 'string', ?string $language = null ) { $centroid = $geometry->centroid(); $lat = $centroid->y(); $lon = $centroid->x(); $url = "http://maps.googleapis.com/maps/api/geocode/json"; /** @noinspection SpellCheckingInspection */ $url .= '?latlng=' . $lat . ',' . $lon; $url .= ($language ? '&language=' . $language : '') . ($apiKey ? '&key=' . $apiKey : ''); $this->result = json_decode(@file_get_contents($url)); if ($this->result->status == 'OK') { if ($returnType == 'string') { return $this->result->results[0]->formatted_address; } elseif ($returnType == 'array') { return $this->result->results[0]->address_components; } elseif ($returnType == 'full') { return $this->result->results[0]; } } elseif ($this->result->status == 'ZERO_RESULTS') { if ($returnType == 'string') { return ''; } if ($returnType == 'array') { return $this->result->results; } } else { if ($this->result->status) { throw new IOException( 'Error in Google Reverse Geocoder: ' . $this->result->status . (isset($this->result->error_message) ? '. ' . $this->result->error_message : '') ); } else { throw new IOException('Unknown error in Google Reverse Geocoder'); } } return ''; } private function getPoint(int $delta = 0): Point { $lat = $this->result->results[$delta]->geometry->location->lat; $lon = $this->result->results[$delta]->geometry->location->lng; return new Point($lon, $lat); } private function getPolygon(int $delta = 0): Polygon { $points = [ $this->getTopLeft($delta), $this->getTopRight($delta), $this->getBottomRight($delta), $this->getBottomLeft($delta), $this->getTopLeft($delta), ]; $outerRing = new LineString($points); return new Polygon([$outerRing]); } private function getTopLeft(int $delta = 0): Point { $lat = $this->result->results[$delta]->geometry->bounds->northeast->lat; $lon = $this->result->results[$delta]->geometry->bounds->southwest->lng; return new Point($lon, $lat); } private function getTopRight(int $delta = 0): Point { $lat = $this->result->results[$delta]->geometry->bounds->northeast->lat; $lon = $this->result->results[$delta]->geometry->bounds->northeast->lng; return new Point($lon, $lat); } private function getBottomLeft(int $delta = 0): Point { $lat = $this->result->results[$delta]->geometry->bounds->southwest->lat; $lon = $this->result->results[$delta]->geometry->bounds->southwest->lng; return new Point($lon, $lat); } private function getBottomRight(int $delta = 0): Point { $lat = $this->result->results[$delta]->geometry->bounds->southwest->lat; $lon = $this->result->results[$delta]->geometry->bounds->northeast->lng; return new Point($lon, $lat); } } <?php namespace geoPHP\Adapter; use geoPHP\Exception\FileFormatException; use geoPHP\Exception\InvalidGeometryException; use geoPHP\Exception\IOException; use geoPHP\geoPHP; use geoPHP\Geometry\{ Collection, Geometry, GeometryCollection, Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon }; /** * WKT (Well Known Text) Adapter */ class WKT implements GeoAdapter { /** * @var bool|null */ protected $hasZ; /** * @var bool|null */ protected $hasM; /** * Determines if the given typeString is a valid WKT geometry type * * @param string $typeString Type to find, eg. "Point", or "LineStringZ" * * @return string|null The geometry type if found or null */ public static function getWktType(string $typeString): ?string { foreach (geoPHP::getGeometryList() as $geom => $type) { if (strtolower((substr($typeString, 0, strlen($geom)))) === $geom) { return $type; } } return null; } /** * Read WKT string into geometry objects * * @param string $wkt A WKT string * * @throws FileFormatException * * @return Geometry */ public function read(string $wkt): Geometry { $this->hasZ = null; $this->hasM = null; $wkt = trim(strtoupper($wkt)); $srid = null; // If the input starts with "SRID=" then read Spatial Reference ID if (preg_match('#^SRID=(\d+);#', $wkt, $m)) { $srid = intval($m[1]); $wkt = substr($wkt, strlen($m[0])); } // If geos is installed, then we take a shortcut and let it parse the WKT if (geoPHP::isGeosInstalled()) { // @codeCoverageIgnoreStart $reader = new \GEOSWKTReader(); $geom = geoPHP::geosToGeometry($reader->read($wkt)); if ($srid) { $geom->setSRID($srid); } return $geom; // @codeCoverageIgnoreEnd } $geometry = $this->parseTypeAndGetData($wkt); if ($srid) { $geometry->setSRID($srid); } return $geometry; } /** * @param string $wkt The WKT input string * * @throws FileFormatException * * @return Geometry */ protected function parseTypeAndGetData($wkt): Geometry { if ( preg_match( '#^(?<type>[A-Z]+?)\s*(?<z>Z*)(?<m>M*)\s*(?:\((?<data>.+)\)|(?<data_empty>EMPTY))$#', $wkt, $matches ) ) { $geometryType = $this->getWktType($matches['type']); if (!$geometryType) { throw new FileFormatException('Invalid WKT type "' . $matches[1] . '."'); } if ($this->hasZ === null && ($matches['z'] === 'Z' || $matches['m'] === 'M')) { $this->hasZ = $matches['z'] === 'Z'; } if ($this->hasM === null && ($matches['z'] === 'Z' || $matches['m'] === 'M')) { $this->hasM = $matches['m'] === 'M'; } $dataString = $matches['data'] ?: $matches['data_empty']; $method = 'parse' . $geometryType; try { return self::$method($dataString); } catch (InvalidGeometryException $eInvalidGeom) { // @phpstan-ignore-line throw new FileFormatException("Invalid WKT {$matches['type']}", $dataString, 0, $eInvalidGeom); } } throw new FileFormatException('Cannot parse WKT.'); } /** * Parses a coordinate sequence and returns a Point. * * @param string $dataString * * @throws FileFormatException Throwed if cannot parse the Point data. * * @return Point */ protected function parseCoordinates(string $dataString): Point { list($x, $y, $coord3, $coord4) = array_merge( explode(' ', (string) preg_replace('#\s+#', ' ', trim($dataString))), [null, null, null, null] ); /** * Set extended dimensions for "old style" WKT, that has no dimension marked. * For example POINT (1 2 3). */ if ($this->hasZ === null && $this->hasM === null) { $this->hasZ = isset($coord3); $this->hasM = isset($coord4); } $z = $m = null; if (isset($coord3)) { if ($this->hasZ) { $z = $coord3; } elseif ($this->hasM) { $m = $coord3; } else { // Maybe later we can implement a stricter mode wich forbids extra coordinates. // throw new FileFormatException( // 'Coordinate dimenstion mismatch. Geometry is not 3D but got Z coordinate.' // ); } } if (isset($coord4)) { if ($this->hasM && $this->hasZ) { $m = $coord4; } else { // throw new FileFormatException( // 'Coordinate dimenstion mismatch. Geometry is not measured but got M coordinate.' // ); } } if ($this->hasZ && $z === null || !$this->hasZ && $z !== null) { throw new FileFormatException( 'Coordinate dimenstion mismatch. Geometry is 3D but no Z coordinate.', $dataString ); } if ($this->hasM && $m === null || !$this->hasM && $m !== null) { throw new FileFormatException( 'Coordinate dimenstion mismatch. Geometry is measured but no M coordinate.', $dataString ); } return new Point($x, $y, $z, $m); } /** * Parses a WKT POINT and returns a Point geometry. * * @param string $dataString * * @throws FileFormatException Throwed if cannot parse the POINT text. * * @return Point */ protected function parsePoint(string $dataString): Point { $dataString = trim($dataString); if ($dataString === 'EMPTY') { return new Point(); } return $this->parseCoordinates($dataString); } /** * Parses coordinate components of a WKT LINESTRING and returns a LineString geometry. * * @param string $dataString * * @throws FileFormatException Throwed if cannot parse the LINESTRING text. * * @return LineString */ protected function parseLineString(string $dataString): LineString { if ($dataString === 'EMPTY') { return new LineString(); } $points = []; foreach (explode(',', $dataString) as $part) { $points[] = $this->parseCoordinates($part); } return new LineString($points); } /** * Parses a WKT POLYGON and returns a Polygon geometry. * * Example WKTs: * empty: POLYGON EMPTY * one ring: POLYGON ((1 2, 3 4, 5 6, 1 2)) * two rings: POLYGON ((1 2, 3 4, 5 6, 1 2), (11 12, 13 14, 15 16, 11 12)) * * @param string $dataString * * @throws FileFormatException Throwed if cannot parse the POLYGON text. * * @return Polygon */ protected function parsePolygon(string $dataString): Polygon { if ($dataString === 'EMPTY') { return new Polygon(); } $rings = []; if (preg_match_all('#\((?<ring>.*?)\)#', $dataString, $matches)) { foreach ($matches['ring'] as $part) { $rings[] = $this->parseLineString($part); } return new Polygon($rings); } else { throw new FileFormatException('Cannot parse WKT POLYGON.', $dataString); } } /** * Parses a WKT MULTIPOINT and returns a MultiPoint geometry. * * Should understand both forms: * OGC style: MULTIPOINT ((1 2), (3 4)) * GEOS style: MULTIPOINT (1 2, 3 4) * * @param string $dataString * * @throws FileFormatException Throwed if cannot parse the MULTIPOINT text. * * @return MultiPoint * * @noinspection PhpUnusedPrivateMethodInspection */ protected function parseMultiPoint(string $dataString): MultiPoint { if ($dataString === 'EMPTY') { return new MultiPoint(); } $points = []; $hasDoubleBraces = null; foreach (explode(',', $dataString) as $part) { if (trim($part) === 'EMPTY') { $points[] = new Point(); } else { // At the first ireation determines if WKT uses "double braces" form. if ($hasDoubleBraces === null) { $hasDoubleBraces = preg_match('#^\(.+\)$#', trim($part)); } // Removes dobule braces. If one of the components uses single brace form, rejects the whole MultiPoint. if ($hasDoubleBraces) { preg_match('#^\((.+)\)$#', trim($part), $matches); $part = $matches[1] ?? null; } if ($part) { $points[] = $this->parsePoint($part); } else { $points = []; break; } } } if ($points) { return new MultiPoint($points); } else { throw new FileFormatException('Cannot parse WKT MULTIPOINT.', $dataString); } } /** * @param string $dataString * * @throws FileFormatException Throwed if cannot parse the MULTILINESTRING text. * * @return MultiLineString * * @noinspection PhpUnusedPrivateMethodInspection */ protected function parseMultiLineString(string $dataString): MultiLineString { if ($dataString === 'EMPTY') { return new MultiLineString(); } $lines = []; if (preg_match_all('#(?<component>\([^)]*?\)|EMPTY)#', $dataString, $matches)) { foreach ($matches['component'] as $component) { // Removes outer braces if any preg_match('#^\((.+)\)$#', $component, $matches2); $lines[] = $this->parseLineString($matches2[1] ?? $component); } } if ($lines) { return new MultiLineString($lines); } else { throw new FileFormatException('Cannot parse WKT MULTILINESTRING.', $dataString); } } /** * @param string $dataString * * @throws FileFormatException Throwed if cannot parse the MULTIPOLYGON text. * * @return MultiPolygon * * @noinspection PhpUnusedPrivateMethodInspection */ protected function parseMultiPolygon(string $dataString): MultiPolygon { if ($dataString === 'EMPTY') { return new MultiPolygon(); } $polygons = []; if (preg_match_all('#(?<component>\((?>[^()]+|(?R))*\)|EMPTY)#', $dataString, $matches)) { foreach ($matches['component'] as $component) { // Removes outer braces if any preg_match('#^\((.+)\)$#', $component, $matches2); $polygons[] = $this->parsePolygon($matches2[1] ?? $component); } } return new MultiPolygon($polygons); } /** * Parses a WKT GEOMETRYCOLLECTION and returns a GeometryCollection geometry. * * Example WKTs: * GEOMETRYCOLLECTION EMPTY * GEOMETRYCOLLECTION (POINT(1 2)) * GEOMETRYCOLLECTION (POINT(1 2), LINESTRING(1 2, 3 4)) * * @param string $dataString * * @throws FileFormatException Throwed if cannot parse the GEOMETRYCOLLECTION text. * * @return GeometryCollection * * @noinspection PhpUnusedPrivateMethodInspection */ protected function parseGeometryCollection(string $dataString): GeometryCollection { if ($dataString === 'EMPTY') { return new GeometryCollection(); } $geometries = []; $offset = 0; while ($offset < strlen($dataString)) { // Matches the first balanced parenthesis group (or term EMPTY) preg_match( '#[A-Z\s]+#', $dataString, $typeMatches, 0, $offset ); $type = $typeMatches[0] ?? ''; $offset += strlen($type); preg_match( '#\((?>[^()]+|(?R))*\)|EMPTY#', $dataString, $dataMatches, 0, $offset ); $data = $dataMatches[0] ?? ''; $offset += strlen($data) + 1; $geometries[] = $this->parseTypeAndGetData(trim(($type . $data))); } return new GeometryCollection($geometries); } /** * Serialize geometries into a WKT string. * * @param Geometry $geometry * * @throws IOException Throwed if the given Geometry is not supported by the WKT writer. * * @return string The WKT string representation of the input geometries. */ public function write(Geometry $geometry): string { // If geos is installed, then we take a shortcut and let it write the WKT if (geoPHP::isGeosInstalled()) { // @codeCoverageIgnoreStart $writer = new \GEOSWKTWriter(); $writer->setRoundingPrecision(14); $writer->setTrim(true); return $writer->write($geometry->getGeos()); // @codeCoverageIgnoreEnd } $this->hasM = $geometry->isMeasured(); $this->hasZ = $geometry->is3D(); if ($geometry->isEmpty()) { return strtoupper($geometry->geometryType()) . ' EMPTY'; } $data = $this->extractData($geometry); $extension = ''; if ($this->hasZ) { $extension .= 'Z'; } if ($this->hasM) { $extension .= 'M'; } return strtoupper($geometry->geometryType()) . ($extension ? ' ' . $extension : '') . ' (' . $data . ')'; } /** * Extract geometry to a WKT string * * @param Geometry|Collection $geometry A Geometry object. * * @throws IOException Throwed if the given Geometry is not supported by the WKT writer. * * @return string The WKT text. */ protected function extractData(Geometry $geometry): string { switch ($geometry->geometryType()) { case Geometry::POINT: return $this->writePoint($geometry); case Geometry::LINE_STRING: return $this->writeLineString($geometry); case Geometry::POLYGON: case Geometry::MULTI_POINT: case Geometry::MULTI_LINE_STRING: case Geometry::MULTI_POLYGON: return $this->writeMulti($geometry); case Geometry::GEOMETRY_COLLECTION: return $this->writeGeometryCollection($geometry); default: // @codeCoverageIgnoreStart throw new IOException('WKT writer does not support ' . $geometry->geometryType() . ' geometry type.'); // @codeCoverageIgnoreEnd } } /** * @param Geometry $geometry * * @return string */ protected function writePoint(Geometry $geometry): string { $pointText = $geometry->x() . ' ' . $geometry->y(); if ($this->hasZ) { $pointText .= ' ' . ($geometry->z() ?: 0); } if ($this->hasM) { $pointText .= ' ' . ($geometry->m() ?: 0); } return trim($pointText); } /** * @param Geometry $geometry * * @return string */ protected function writeLineString(Geometry $geometry): string { $parts = []; foreach ($geometry->getComponents() as $component) { $parts[] = $this->extractData($component); } return implode(', ', $parts); } /** * @param Geometry $geometry * * @return string */ protected function writeMulti(Geometry $geometry): string { $parts = []; foreach ($geometry->getComponents() as $component) { if ($component->isEmpty()) { $parts[] = 'EMPTY'; } else { $parts[] = '(' . $this->extractData($component) . ')'; } } return implode(', ', $parts); } /** * @param Geometry $geometry * * @return string */ protected function writeGeometryCollection(Geometry $geometry): string { $parts = []; foreach ($geometry->getComponents() as $component) { $extension = ''; if ($this->hasZ) { $extension .= 'Z'; } if ($this->hasM) { $extension .= 'M'; } $data = $this->extractData($component); $parts[] = strtoupper($component->geometryType()) . ($extension ? ' ' . $extension : '') . ($data ? ' (' . $data . ')' : ' EMPTY'); } return implode(', ', $parts); } } <?php namespace geoPHP\Adapter; use geoPHP\Exception\IOException; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\GeometryCollection; use geoPHP\Geometry\Point; use geoPHP\Geometry\MultiPoint; use geoPHP\Geometry\LineString; use geoPHP\Geometry\MultiGeometry; use geoPHP\Geometry\MultiLineString; use geoPHP\Geometry\Polygon; use geoPHP\Geometry\MultiPolygon; use InvalidArgumentException; /* * (c) Patrick Hayes * * This code is open-source and licenced under the Modified BSD License. * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * PHP Geometry/WKB encoder/decoder * Reader can decode EWKB too. Writer always encodes valid WKBs * */ class WKB implements GeoAdapter { const Z_MASK = 0x80000000; const M_MASK = 0x40000000; const SRID_MASK = 0x20000000; const WKB_XDR = 1; const WKB_NDR = 0; /** @var bool */ protected $hasZ = false; /** @var bool */ protected $hasM = false; /** @var bool */ protected $hasSRID = false; /** @var int|null */ protected $SRID = null; /** @var int */ protected $dimension = 2; /** @var BinaryReader $reader */ protected $reader; /** @var BinaryWriter $writer */ protected $writer; /** @var array<string, int> Maps Geometry types to WKB type codes */ public static $typeMap = [ Geometry::POINT => 1, Geometry::LINE_STRING => 2, Geometry::POLYGON => 3, Geometry::MULTI_POINT => 4, Geometry::MULTI_LINE_STRING => 5, Geometry::MULTI_POLYGON => 6, Geometry::GEOMETRY_COLLECTION => 7, //Not supported types: Geometry::CIRCULAR_STRING => 8, Geometry::COMPOUND_CURVE => 9, Geometry::CURVE_POLYGON => 10, Geometry::MULTI_CURVE => 11, Geometry::MULTI_SURFACE => 12, Geometry::CURVE => 13, Geometry::SURFACE => 14, Geometry::POLYHEDRAL_SURFACE => 15, Geometry::TIN => 16, Geometry::TRIANGLE => 17, ]; /** * Read WKB into geometry objects * * @param string $wkb Well-known-binary string * @param bool $isHexString If this is a hexadecimal string that is in need of packing * * @return Geometry * * @throws IOException */ public function read(string $wkb, bool $isHexString = false): Geometry { if ($isHexString) { $wkb = pack('H*', $wkb); } if (empty($wkb)) { throw new IOException('Cannot read empty WKB geometry. Found ' . gettype($wkb)); } $this->reader = new BinaryReader($wkb); $geometry = $this->getGeometry(); $this->reader->close(); return $geometry; } /** * @return Geometry * @throws \Exception * @throws IOException */ protected function getGeometry() { $this->hasZ = false; $this->hasM = false; $SRID = null; $this->reader->setEndianness( $this->reader->readSInt8() === self::WKB_XDR ? BinaryReader::LITTLE_ENDIAN : BinaryReader::BIG_ENDIAN ); $wkbType = $this->reader->readUInt32(); if (($wkbType & $this::SRID_MASK) === $this::SRID_MASK) { $SRID = $this->reader->readUInt32(); } $geometryType = null; if ($wkbType >= 1000 && $wkbType < 2000) { $this->hasZ = true; $geometryType = $wkbType - 1000; } elseif ($wkbType >= 2000 && $wkbType < 3000) { $this->hasM = true; $geometryType = $wkbType - 2000; } elseif ($wkbType >= 3000 && $wkbType < 4000) { $this->hasZ = true; $this->hasM = true; $geometryType = $wkbType - 3000; } if ($wkbType & $this::Z_MASK) { $this->hasZ = true; } if ($wkbType & $this::M_MASK) { $this->hasM = true; } $this->dimension = 2 + ($this->hasZ ? 1 : 0) + ($this->hasM ? 1 : 0); if (!$geometryType) { $geometryType = $wkbType & 0xF; // remove any masks from type } $geometry = null; switch ($geometryType) { case 1: $geometry = $this->getPoint(); break; case 2: $geometry = $this->getLineString(); break; case 3: $geometry = $this->getPolygon(); break; case 4: $geometry = $this->getMulti('Point'); break; case 5: $geometry = $this->getMulti('LineString'); break; case 6: $geometry = $this->getMulti('Polygon'); break; case 7: $geometry = $this->getMulti('Geometry'); break; default: throw new IOException( 'Geometry type ' . $geometryType . ' (' . (array_search($geometryType, self::$typeMap) ?: 'unknown') . ') not supported' ); } if ($SRID) { $geometry->setSRID($SRID); } return $geometry; } protected function getPoint(): Point { $coordinates = $this->reader->readDoubles($this->dimension * 8); if (is_nan($coordinates[0]) || is_nan($coordinates[1])) { return new Point(); } switch (count($coordinates)) { case 2: $point = new Point($coordinates[0], $coordinates[1]); break; case 3: if (is_nan($coordinates[2])) { $coordinates[2] = null; } $point = $this->hasZ ? new Point($coordinates[0], $coordinates[1], $coordinates[2]) : new Point($coordinates[0], $coordinates[1], null, $coordinates[2]); break; case 4: if (is_nan($coordinates[2])) { $coordinates[2] = null; } if (is_nan($coordinates[3])) { $coordinates[3] = null; } $point = new Point($coordinates[0], $coordinates[1], $coordinates[2], $coordinates[3]); break; default: $point = new Point(); } return $point; } protected function getLineString(): LineString { // Get the number of points expected in this string out of the first 4 bytes $lineLength = $this->reader->readUInt32(); // Return an empty linestring if there is no line-length if (!$lineLength) { return new LineString(); } $components = []; for ($i = 0; $i < $lineLength; ++$i) { $point = $this->getPoint(); $components[] = $point; } return new LineString($components); } protected function getPolygon(): Polygon { // Get the number of linestring expected in this poly out of the first 4 bytes $polyLength = $this->reader->readUInt32(); $components = []; $i = 1; while ($i <= $polyLength) { $ring = $this->getLineString(); if (!$ring->isEmpty()) { $components[] = $ring; } $i++; } return new Polygon($components); } protected function getMulti(string $type): MultiGeometry { // Get the number of items expected in this multi out of the first 4 bytes $multiLength = $this->reader->readUInt32(); $components = []; for ($i = 0; $i < $multiLength; $i++) { $component = $this->getGeometry(); $component->setSRID(null); $components[] = $component; } switch ($type) { case 'Point': /** @var Point[] $components */ return new MultiPoint($components); case 'LineString': /** @var LineString[] $components */ return new MultiLineString($components); case 'Polygon': /** @var Polygon[] $components */ return new MultiPolygon($components); case 'Geometry': return new GeometryCollection($components); } throw new InvalidArgumentException("Unknown multi-geometry type '$type'."); } /** * Serialize geometries into WKB string. * * @param Geometry $geometry The geometry * @param boolean $writeAsHex Write the result in binary or hexadecimal system * @param boolean $bigEndian Write in BigEndian or LittleEndian byte order * * @return string The WKB string representation of the input geometries */ public function write(Geometry $geometry, bool $writeAsHex = false, bool $bigEndian = false): string { $this->writer = new BinaryWriter($bigEndian ? BinaryWriter::BIG_ENDIAN : BinaryWriter::LITTLE_ENDIAN); $wkb = $this->writeGeometry($geometry); return $writeAsHex ? current(unpack('H*', $wkb)) : $wkb; } /** * @param Geometry $geometry * @return string */ protected function writeGeometry($geometry) { $this->hasZ = $geometry->is3D(); $this->hasM = $geometry->isMeasured(); $wkb = $this->writer->writeSInt8($this->writer->isBigEndian() ? self::WKB_NDR : self::WKB_XDR); $wkb .= $this->writeType($geometry); switch ($geometry->geometryType()) { case Geometry::POINT: /** @var Point $geometry */ $wkb .= $this->writePoint($geometry); break; case Geometry::LINE_STRING: /** @var LineString $geometry */ $wkb .= $this->writeLineString($geometry); break; case Geometry::POLYGON: /** @var Polygon $geometry */ $wkb .= $this->writePolygon($geometry); break; case Geometry::MULTI_POINT: /** @var MultiPoint $geometry */ $wkb .= $this->writeMulti($geometry); break; case Geometry::MULTI_LINE_STRING: /** @var MultiLineString $geometry */ $wkb .= $this->writeMulti($geometry); break; case Geometry::MULTI_POLYGON: /** @var MultiPolygon $geometry */ $wkb .= $this->writeMulti($geometry); break; case Geometry::GEOMETRY_COLLECTION: /** @var GeometryCollection $geometry */ $wkb .= $this->writeMulti($geometry); break; } return $wkb; } /** * @param Point $point * @return string */ protected function writePoint(Point $point): string { if ($point->isEmpty()) { /* * Geos represents empty geometry as NaN * @see https://trac.osgeo.org/geos/changeset/466cff135c8e504632ae38b79a1348dbadb390f1/git */ return $this->writer->writeDouble(NAN) . $this->writer->writeDouble(NAN); } $wkb = $this->writer->writeDouble($point->x()) . $this->writer->writeDouble($point->y()); if ($this->hasZ) { $wkb .= $this->writer->writeDouble($point->z()); } if ($this->hasM) { $wkb .= $this->writer->writeDouble($point->m()); } return $wkb; } /** * @param LineString $line * @return string */ protected function writeLineString(LineString $line): string { // Set the number of points in this line $wkb = $this->writer->writeUInt32($line->numPoints()); // Set the coords foreach ($line->getComponents() as $i => $point) { $wkb .= $this->writePoint($point); } return $wkb; } /** * @param Polygon $poly * @return string */ protected function writePolygon(Polygon $poly): string { // Set the number of lines in this poly $wkb = $this->writer->writeUInt32($poly->numGeometries()); // Write the lines foreach ($poly->getComponents() as $line) { $wkb .= $this->writeLineString($line); } return $wkb; } /** * @param MultiGeometry $geometry * @return string */ protected function writeMulti(MultiGeometry $geometry): string { // Set the number of components $wkb = $this->writer->writeUInt32($geometry->numGeometries()); // Write the components foreach ($geometry->getComponents() as $component) { $wkb .= $this->writeGeometry($component); } return $wkb; } /** * @param Geometry $geometry * @param bool $writeSRID * @return string */ protected function writeType(Geometry $geometry, bool $writeSRID = false): string { $type = self::$typeMap[$geometry->geometryType()]; // Binary OR to mix in additional properties if ($this->hasZ) { $type = $type | $this::Z_MASK; } if ($this->hasM) { $type = $type | $this::M_MASK; } if ($geometry->getSRID() && $writeSRID) { $type = $type | $this::SRID_MASK; } return $this->writer->writeUInt32($type) . ($geometry->getSRID() && $writeSRID ? $this->writer->writeUInt32($this->SRID) : ''); } } <?php /** * This file contains the BinaryReader class. * For more information see the class description below. * * @author Peter Bathory <peter.bathory@cartographia.hu> * @since 2016-02-18 * * This code is open-source and licenced under the Modified BSD License. * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace geoPHP\Adapter; /** * Helper class BinaryReader * * A simple binary reader supporting both byte orders */ class BinaryReader { const BIG_ENDIAN = 0; const LITTLE_ENDIAN = 1; /** * @var resource */ private $buffer; /** * @var int */ private $endianness = 0; /** * BinaryReader constructor. * Opens a memory buffer with the given input * * @param string $input */ public function __construct(string $input) { // if (@is_readable($input)) { // $this->buffer = fopen($input, 'r+'); // } else { $this->buffer = fopen('php://memory', 'x+'); fwrite($this->buffer, $input); fseek($this->buffer, 0); // } } /** * Closes the memory buffer */ public function close(): void { fclose($this->buffer); } /** * @param int $endian self::BIG_ENDIAN or self::LITTLE_ENDIAN */ public function setEndianness(int $endian): void { $this->endianness = $endian === self::BIG_ENDIAN ? self::BIG_ENDIAN : self::LITTLE_ENDIAN; } /** * @return int Returns 0 if reader is in BigEndian mode or 1 if in LittleEndian mode */ public function getEndianness(): int { return $this->endianness; } /** * Reads a signed 8-bit integer from the buffer * @return int|null */ public function readSInt8(): ?int { $char = fread($this->buffer, 1); return $char !== '' ? current(unpack("c", $char)) : null; } /** * Reads an unsigned 8-bit integer from the buffer * @return int|null */ public function readUInt8(): ?int { $char = fread($this->buffer, 1); return $char !== '' ? current(unpack("C", $char)) : null; } /** * Reads an unsigned 32-bit integer from the buffer * @return int|null */ public function readUInt32() { $int32 = fread($this->buffer, 4); return $int32 !== '' ? current(unpack($this->endianness == self::LITTLE_ENDIAN ? 'V' : 'N', $int32)) : null; } /** * Reads one or more double values from the buffer * @param int $length How many double values to read. Default is 1 * @return float[] */ public function readDoubles($length = 1) { $bin = fread($this->buffer, $length); return $this->endianness == self::LITTLE_ENDIAN ? array_values(unpack("d*", $bin)) : array_reverse(unpack("d*", strrev($bin))); } /** * Reads an unsigned base-128 varint from the buffer * * Ported from https://github.com/cschwarz/wkx/blob/master/lib/binaryreader.js * * @return int */ public function readUVarInt() { $result = 0; $bytesRead = 0; do { $nextByte = $this->readUInt8(); $result += ($nextByte & 0x7F) << (7 * $bytesRead); $bytesRead++; } while ($nextByte >= 0x80); return $result; } /** * Reads a signed base-128 varint from the buffer * * @return int */ public function readSVarInt() { return self::zigZagDecode($this->readUVarInt()); } /** * ZigZag decoding maps unsigned integers to signed integers * * @param int $value Encrypted positive integer value * @return int Decoded signed integer */ public static function zigZagDecode($value) { return ($value & 1) === 0 ? $value >> 1 : -($value >> 1) - 1; } } <?php namespace geoPHP\Adapter; use geoPHP\Exception\InvalidXmlException; use geoPHP\Geometry\Collection; use geoPHP\geoPHP; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\GeometryCollection; use geoPHP\Geometry\Point; use geoPHP\Geometry\LineString; use geoPHP\Geometry\Polygon; use geoPHP\Exception\IOException; use geoPHP\Geometry\MultiGeometry; /* * Copyright (c) Patrick Hayes * * This code is open-source and licenced under the Modified BSD License. * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * PHP Geometry/GeoRSS encoder/decoder */ class GeoRSS implements GeoAdapter { /** * @var \DOMDocument */ protected $xmlObject; /** * @var string Name-space string. eg "georss:" */ private $nss = ''; /** * Read GeoRSS string into geometry objects. * * @param string $georss An XML feed containing geoRSS. * * @return Geometry */ public function read(string $georss): Geometry { // Change to lower-case, strip all CDATA, and de-namespace $georss = strtolower($georss); $georss = preg_replace('/<!\[cdata\[(.*?)\]\]>/s', '', $georss); // Load into DOMDocument $this->xmlObject = new \DOMDocument(); $loadSuccess = @$this->xmlObject->loadXML($georss); if (!$loadSuccess) { throw new InvalidXmlException(); } $geometries = array_merge( $this->parsePoints(), $this->parseLines(), $this->parsePolygons(), $this->parseBoxes(), $this->parseCircles() ); return geoPHP::geometryReduce($geometries); } /** * @param string $string * @return Point[] */ protected function getPointsFromCoordinates(string $string): array { $coordinates = []; $latitudeAndLongitude = explode(' ', $string); $lat = 0; foreach ($latitudeAndLongitude as $key => $item) { if (!($key % 2)) { // It's a latitude $lat = is_numeric($item) ? $item : null; } else { // It's a longitude $lon = is_numeric($item) ? $item : null; $coordinates[] = new Point($lon, $lat); } } return $coordinates; } /** * @return Point[] */ protected function parsePoints(): array { $points = []; $pointElements = $this->xmlObject->getElementsByTagName('point'); foreach ($pointElements as $pt) { $pointArray = $this->getPointsFromCoordinates(trim($pt->firstChild->nodeValue)); $points[] = !empty($pointArray) ? $pointArray[0] : new Point(); } return $points; } /** * @return LineString[] */ protected function parseLines(): array { $lines = []; $lineElements = $this->xmlObject->getElementsByTagName('line'); foreach ($lineElements as $line) { $components = $this->getPointsFromCoordinates(trim($line->firstChild->nodeValue)); $lines[] = new LineString($components); } return $lines; } /** * @return Polygon[] */ protected function parsePolygons(): array { $polygons = []; $polygonElements = $this->xmlObject->getElementsByTagName('polygon'); foreach ($polygonElements as $polygon) { /** @noinspection PhpUndefinedMethodInspection */ if ($polygon->hasChildNodes()) { $points = $this->getPointsFromCoordinates(trim($polygon->firstChild->nodeValue)); $exteriorRing = new LineString($points); $polygons[] = new Polygon([$exteriorRing]); } else { // It's an EMPTY polygon $polygons[] = new Polygon(); } } return $polygons; } /** * Boxes are rendered into polygons * * @return Polygon[] */ protected function parseBoxes(): array { $polygons = []; $boxElements = $this->xmlObject->getElementsByTagName('box'); foreach ($boxElements as $box) { $parts = explode(' ', trim($box->firstChild->nodeValue)); $components = [ new Point($parts[3], $parts[2]), new Point($parts[3], $parts[0]), new Point($parts[1], $parts[0]), new Point($parts[1], $parts[2]), new Point($parts[3], $parts[2]), ]; $exteriorRing = new LineString($components); $polygons[] = new Polygon([$exteriorRing]); } return $polygons; } /** * Circles are rendered into points. * * @@TODO: Add good support once we have circular-string geometry support. * * @return Point[] */ protected function parseCircles(): array { $points = []; $circleElements = $this->xmlObject->getElementsByTagName('circle'); foreach ($circleElements as $circle) { $parts = explode(' ', trim($circle->firstChild->nodeValue)); $points[] = new Point($parts[1], $parts[0]); } return $points; } /** * Serialize geometries into a GeoRSS string. * * @param Geometry $geometry * @param boolean|string $namespace * @return string The georss string representation of the input geometries */ public function write(Geometry $geometry, $namespace = false): string { if ($namespace) { $this->nss = $namespace . ':'; } return $this->geometryToGeoRSS($geometry) ?: ''; } /** * @param Geometry $geometry * @return string|null */ protected function geometryToGeoRSS(Geometry $geometry): ?string { $type = $geometry->geometryType(); switch ($type) { case Geometry::POINT: /** @var Point $geometry */ return $this->pointToGeoRSS($geometry); case Geometry::LINE_STRING: /** @var LineString $geometry */ return $this->linestringToGeoRSS($geometry); case Geometry::POLYGON: /** @var Polygon $geometry */ return $this->polygonToGeoRSS($geometry); case Geometry::MULTI_POINT: case Geometry::MULTI_LINE_STRING: case Geometry::MULTI_POLYGON: case Geometry::GEOMETRY_COLLECTION: /** @var MultiGeometry $geometry */ return $this->collectionToGeoRSS($geometry); default: return null; } } /** * @param Point $geometry * @return string */ private function pointToGeoRSS(Point $geometry): string { return '<' . $this->nss . 'point>' . $geometry->y() . ' ' . $geometry->x() . '</' . $this->nss . 'point>'; } /** * @param LineString $geometry * @return string */ private function linestringToGeoRSS(LineString $geometry): string { $output = '<' . $this->nss . 'line>'; foreach ($geometry->getComponents() as $k => $point) { $output .= $point->y() . ' ' . $point->x(); if ($k < ($geometry->numGeometries() - 1)) { $output .= ' '; } } $output .= '</' . $this->nss . 'line>'; return $output; } /** * @param Polygon $geometry * @return string */ private function polygonToGeoRSS(Polygon $geometry): string { $output = '<' . $this->nss . 'polygon>'; $exteriorRing = $geometry->exteriorRing(); foreach ($exteriorRing->getComponents() as $k => $point) { $output .= $point->y() . ' ' . $point->x(); if ($k < ($exteriorRing->numGeometries() - 1)) { $output .= ' '; } } $output .= '</' . $this->nss . 'polygon>'; return $output; } /** * @param MultiGeometry $geometry * @return string */ public function collectionToGeoRSS(MultiGeometry $geometry): string { $georss = '<' . $this->nss . 'where>'; $components = $geometry->getComponents(); foreach ($components as $component) { $georss .= $this->geometryToGeoRSS($component); } $georss .= '</' . $this->nss . 'where>'; return $georss; } } <?php namespace geoPHP\Adapter; use geoPHP\Geometry\Geometry; /* * (c) Patrick Hayes 2011 * * This code is open-source and licenced under the Modified BSD License. * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * GeoAdapter : Interface of adapters * for reading and writing to and from Geometry objects * */ interface GeoAdapter { /** * Read input and return a Geometry * * @param string $input * @return Geometry */ public function read(string $input): Geometry; /** * Write out a Geometry in the adapter's format * * @param Geometry $geometry * @return string */ public function write(Geometry $geometry): string; } <?php /* * @author Báthory Péter * @since 2016-02-27 * * This code is open-source and licenced under the Modified BSD License. * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace geoPHP\Adapter; use geoPHP\Exception\FileFormatException; use geoPHP\Exception\InvalidXmlException; use geoPHP\Exception\IOException; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\GeometryCollection; use geoPHP\Geometry\Point; use geoPHP\Geometry\MultiPoint; use geoPHP\Geometry\LineString; use geoPHP\Geometry\MultiGeometry; use geoPHP\Geometry\MultiLineString; use geoPHP\Geometry\Polygon; use geoPHP\Geometry\MultiPolygon; /** * PHP Geometry <-> OpenStreetMap XML encoder/decoder * * This adapter is not ready yet. It lacks a relation writer, and the reader has problems with invalid multipolygons * Since geoPHP doesn't support metadata, it cannot read and write OSM tags. */ class OSM implements GeoAdapter { const OSM_COORDINATE_PRECISION = '%.7f'; const OSM_API_URL = 'http://openstreetmap.org/api/0.6/'; /** @var \DOMDocument */ protected $xmlObj; /** * @var array<array{point: Point, assigned: bool, tags: array<mixed>, id?: int, used?: bool}>|array{} */ protected $nodes = []; /** * @var array<array{nodes: array<mixed>, assigned: bool, tags: array<mixed>, isRng: bool}>|array{} */ protected $ways = []; /** * @var int */ protected $idCounter = 0; /** * Read OpenStreetMap XML string into geometry objects * * @param string $osm An OSM XML string * * @return Geometry|GeometryCollection * @throws \Exception */ public function read(string $osm): Geometry { // Load into DOMDocument $this->xmlObj = new \DOMDocument(); $loadSuccess = @$this->xmlObj->loadXML($osm); if (!$loadSuccess) { throw new InvalidXmlException(); } try { $geom = $this->geomFromXML(); } catch (\Exception $e) { throw new FileFormatException("Cannot read geometries from OSM XML: " . $e->getMessage()); } return $geom; } protected function geomFromXML(): Geometry { $geometries = []; // Processing OSM Nodes $nodes = []; foreach ($this->xmlObj->getElementsByTagName('node') as $node) { /** @var \DOMElement $node */ $lat = $node->attributes->getNamedItem('lat')->nodeValue; $lon = $node->attributes->getNamedItem('lon')->nodeValue; $id = intval($node->attributes->getNamedItem('id')->nodeValue); $tags = []; foreach ($node->getElementsByTagName('tag') as $tag) { $key = $tag->attributes->getNamedItem('k')->nodeValue; if ($key === 'source' || $key === 'fixme' || $key === 'created_by') { continue; } $tags[$key] = $tag->attributes->getNamedItem('v')->nodeValue; } $nodes[$id] = [ 'point' => new Point($lon, $lat), 'assigned' => false, 'tags' => $tags ]; } if (empty($nodes)) { return new GeometryCollection(); } // Processing OSM Ways $ways = []; foreach ($this->xmlObj->getElementsByTagName('way') as $way) { /** @var \DOMElement $way */ $id = intval($way->attributes->getNamedItem('id')->nodeValue); $wayNodes = []; foreach ($way->getElementsByTagName('nd') as $wayNode) { $ref = intval($wayNode->attributes->getNamedItem('ref')->nodeValue); if (isset($nodes[$ref])) { $nodes[$ref]['assigned'] = true; $wayNodes[] = $ref; } } $tags = []; foreach ($way->getElementsByTagName('tag') as $tag) { $key = $tag->attributes->getNamedItem('k')->nodeValue; if ($key === 'source' || $key === 'fixme' || $key === 'created_by') { continue; } $tags[$key] = $tag->attributes->getNamedItem('v')->nodeValue; } if (count($wayNodes) >= 2) { $ways[$id] = [ 'nodes' => $wayNodes, 'assigned' => false, 'tags' => $tags, 'isRing' => ($wayNodes[0] === $wayNodes[count($wayNodes) - 1]) ]; } } // Processing OSM Relations /** @var \DOMElement $relation */ foreach ($this->xmlObj->getElementsByTagName('relation') as $relation) { /** @var Point[] */ $relationPoints = []; /** @var LineString[] */ $relationLines = []; /** @var Polygon[] */ $relationPolygons = []; static $polygonalTypes = ['multipolygon', 'boundary']; static $linearTypes = ['route', 'waterway']; $relationType = null; foreach ($relation->getElementsByTagName('tag') as $tag) { if ($tag->attributes->getNamedItem('k')->nodeValue == 'type') { $relationType = $tag->attributes->getNamedItem('v')->nodeValue; } } // Collect relation members /** @var array<int, array<mixed>> $relationWays */ $relationWays = []; foreach ($relation->getElementsByTagName('member') as $member) { $memberType = $member->attributes->getNamedItem('type')->nodeValue; $ref = (int) $member->attributes->getNamedItem('ref')->nodeValue; if ($memberType === 'node' && isset($nodes[$ref])) { $nodes[$ref]['assigned'] = true; $relationPoints[] = $nodes[$ref]['point']; } if ($memberType === 'way' && isset($ways[$ref])) { $ways[$ref]['assigned'] = true; $relationWays[$ref] = $ways[$ref]['nodes']; } } if (in_array($relationType, $polygonalTypes)) { $relationPolygons = $this->processMultipolygon($relationWays, $nodes); } if (in_array($relationType, $linearTypes)) { $relationLines = $this->processRoutes($relationWays, $nodes); } // Assemble relation geometries $geometryCollection = []; if (!empty($relationPolygons)) { $geometryCollection[] = count($relationPolygons) == 1 ? $relationPolygons[0] : new MultiPolygon($relationPolygons); } if (!empty($relationLines)) { $geometryCollection[] = count($relationLines) == 1 ? $relationLines[0] : new MultiLineString($relationLines); } if (!empty($relationPoints)) { $geometryCollection[] = count($relationPoints) == 1 ? $relationPoints[0] : new MultiPoint($relationPoints); } if (!empty($geometryCollection)) { $geometries[] = count($geometryCollection) == 1 ? $geometryCollection[0] : new GeometryCollection($geometryCollection); } } // Process ways foreach ($ways as $way) { if ( (!$way['assigned'] || !empty($way['tags'])) && !isset($way['tags']['boundary']) && (!isset($way['tags']['natural']) || $way['tags']['natural'] !== 'mountain_range') ) { $linePoints = []; foreach ($way['nodes'] as $wayNode) { $linePoints[] = $nodes[$wayNode]['point']; } $line = new LineString($linePoints); if ($way['isRing']) { $polygon = new Polygon([$line]); if ($polygon->isSimple()) { $geometries[] = $polygon; } else { $geometries[] = $line; } } else { $geometries[] = $line; } } } foreach ($nodes as $node) { if (!$node['assigned'] || !empty($node['tags'])) { $geometries[] = $node['point']; } } return count($geometries) == 1 ? $geometries[0] : new GeometryCollection($geometries); } /** * @param array<array<mixed>> $relationWays * @param array<array<mixed>> $nodes * @return LineString[] */ protected function processRoutes(array &$relationWays, array &$nodes): array { // Construct lines /** @var LineString[] $lineStrings */ $lineStrings = []; while (count($relationWays)) { $line = array_shift($relationWays); if ($line[0] !== $line[count($line) - 1]) { do { $waysAdded = 0; foreach ($relationWays as $id => $wayNodes) { // Last node of ring = first node of way => put way to the end of ring if ($line[count($line) - 1] === $wayNodes[0]) { $line = array_merge($line, array_slice($wayNodes, 1)); unset($relationWays[$id]); $waysAdded++; // Last node of ring = last node of way => reverse way and put to the end of ring } elseif ($line[count($line) - 1] === $wayNodes[count($wayNodes) - 1]) { $line = array_merge($line, array_slice(array_reverse($wayNodes), 1)); unset($relationWays[$id]); $waysAdded++; // First node of ring = last node of way => put way to the beginning of ring } elseif ($line[0] === $wayNodes[count($wayNodes) - 1]) { $line = array_merge(array_slice($wayNodes, 0, count($wayNodes) - 1), $line); unset($relationWays[$id]); $waysAdded++; // First node of ring = first node of way => reverse way and put to the beginning of ring } elseif ($line[0] === $wayNodes[0]) { $line = array_merge(array_reverse(array_slice($wayNodes, 1)), $line); unset($relationWays[$id]); $waysAdded++; } } // If line members are not ordered, we need to repeat end matching some times } while ($waysAdded > 0); } // Create the new LineString $linePoints = []; foreach ($line as $lineNode) { $linePoints[] = $nodes[$lineNode]['point']; } $lineStrings[] = new LineString($linePoints); } return $lineStrings; } /** * @param array<array<mixed>> $relationWays * @param array<array<mixed>> $nodes * @return Polygon[] */ protected function processMultipolygon(array &$relationWays, array &$nodes): array { /* TODO: what to do with broken rings? * I propose to force-close if start -> end point distance is less then 10% of line length, otherwise drop it. * But if dropped, its inner ring will be outers, which is not good. * We should save the role for each ring (outer, inner, mixed) during ring creation * and check it during grouping rings. */ // Construct rings /** @var Polygon[] $rings */ $rings = []; while (!empty($relationWays)) { $ring = array_shift($relationWays); if ($ring[0] !== $ring[count($ring) - 1]) { do { $waysAdded = 0; foreach ($relationWays as $id => $wayNodes) { // Last node of ring = first node of way => put way to the end of ring if ($ring[count($ring) - 1] === $wayNodes[0]) { $ring = array_merge($ring, array_slice($wayNodes, 1)); unset($relationWays[$id]); $waysAdded++; // Last node of ring = last node of way => reverse way and put to the end of ring } elseif ($ring[count($ring) - 1] === $wayNodes[count($wayNodes) - 1]) { $ring = array_merge($ring, array_slice(array_reverse($wayNodes), 1)); unset($relationWays[$id]); $waysAdded++; // First node of ring = last node of way => put way to the beginning of ring } elseif ($ring[0] === $wayNodes[count($wayNodes) - 1]) { $ring = array_merge(array_slice($wayNodes, 0, count($wayNodes) - 1), $ring); unset($relationWays[$id]); $waysAdded++; // First node of ring = first node of way => reverse way and put to the beginning of ring } elseif ($ring[0] === $wayNodes[0]) { $ring = array_merge(array_reverse(array_slice($wayNodes, 1)), $ring); unset($relationWays[$id]); $waysAdded++; } } // If ring members are not ordered, we need to repeat end matching some times } while ($waysAdded > 0 && $ring[0] !== $ring[count($ring) - 1]); } // Create the new Polygon if ($ring[0] === $ring[count($ring) - 1]) { $ringPoints = []; foreach ($ring as $ringNode) { $ringPoints[] = $nodes[$ringNode]['point']; } $newPolygon = new Polygon([new LineString($ringPoints)]); if ($newPolygon->isSimple()) { $rings[] = $newPolygon; } } } // Calculate containment $containment = array_fill(0, count($rings), array_fill(0, count($rings), false)); foreach ($rings as $i => $ring) { foreach ($rings as $j => $ring2) { if ($i !== $j && $ring->contains($ring2)) { $containment[$i][$j] = true; } } } $containmentCount = count($containment); /* print '   '; for($i=0; $i<count($rings); $i++) { print $rings[$i]->getNumberOfPoints() . ' '; } print "<br>"; for($i=0; $i<count($rings); $i++) { print $rings[$i]->getNumberOfPoints() . ' '; for($j=0; $j<count($rings); $j++) { print ($containment[$i][$j] ? '1' : '0') . ' '; } print "<br>"; }*/ // Group rings (outers and inners) /** @var boolean[] $found */ $found = array_fill(0, $containmentCount, false); $foundCount = 0; $round = 0; /** @var int[][] $polygonsRingIds */ $polygonsRingIds = []; /** @var Polygon[] $relationPolygons */ $relationPolygons = []; while ($foundCount < $containmentCount && $round < 100) { $ringsFound = []; for ($i = 0; $i < $containmentCount; $i++) { if ($found[$i]) { continue; } $containCount = 0; for ($j = 0; $j < count($containment[$i]); $j++) { if (!$found[$j]) { $containCount += $containment[$j][$i]; } } if ($containCount === 0) { $ringsFound[] = $i; } } if ($round % 2 === 0) { $polygonsRingIds = []; } foreach ($ringsFound as $ringId) { $found[$ringId] = true; $foundCount++; if ($round % 2 === 1) { foreach ($polygonsRingIds as $outerId => $polygon) { if ($containment[$outerId][$ringId]) { $polygonsRingIds[$outerId][] = $ringId; } } } else { $polygonsRingIds[$ringId] = [0 => $ringId]; } } if ($round % 2 === 1 || $foundCount === $containmentCount) { foreach ($polygonsRingIds as $k => $ringGroup) { $linearRings = []; foreach ($ringGroup as $polygonRing) { $linearRings[] = $rings[$polygonRing]->exteriorRing(); } $relationPolygons[] = new Polygon($linearRings); } } ++$round; } return $relationPolygons; } public function write(Geometry $geometry): string { $this->processGeometry($geometry); $osm = "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' upload='false' generator='geoPHP'>\n"; foreach ($this->nodes as $latlon => $node) { $latlon = explode('_', $latlon); $osm .= " <node id='{$node['id']}' visible='true' lat='$latlon[0]' lon='$latlon[1]' />\n"; } foreach ($this->ways as $wayId => $way) { $osm .= " <way id='{$wayId}' visible='true'>\n"; foreach ($way as $nodeId) { $osm .= " <nd ref='{$nodeId}' />\n"; } $osm .= " </way>\n"; } $osm .= "</osm>"; return $osm; } /** * @param Geometry $geometry */ protected function processGeometry(Geometry $geometry): void { if (!$geometry->isEmpty()) { switch ($geometry->geometryType()) { case Geometry::POINT: /** @var Point $geometry */ $this->processPoint($geometry); break; case Geometry::LINE_STRING: /** @var LineString $geometry */ $this->processLineString($geometry); break; case Geometry::POLYGON: /** @var Polygon $geometry */ $this->processPolygon($geometry); break; case Geometry::MULTI_POINT: case Geometry::MULTI_LINE_STRING: case Geometry::MULTI_POLYGON: case Geometry::GEOMETRY_COLLECTION: /** @var MultiGeometry $geometry */ $this->processCollection($geometry); break; } } } /** * @param Point $point * @param bool|false $isWayPoint * @return int */ protected function processPoint(Point $point, bool $isWayPoint = false): int { $nodePosition = sprintf( self::OSM_COORDINATE_PRECISION . '_' . self::OSM_COORDINATE_PRECISION, $point->y(), $point->x() ); if (!isset($this->nodes[$nodePosition])) { $this->nodes[$nodePosition] = ['id' => --$this->idCounter, "used" => $isWayPoint]; return $this->idCounter; } else { if ($isWayPoint) { $this->nodes[$nodePosition]['used'] = true; } return $this->nodes[$nodePosition]['id']; } } /** * @param LineString $line */ protected function processLineString(LineString $line): void { $processedNodes = []; foreach ($line->getPoints() as $point) { $processedNodes[] = $this->processPoint($point, true); } $this->ways[--$this->idCounter] = $processedNodes; } /** * @param Polygon $polygon */ protected function processPolygon(Polygon $polygon): void { // TODO: Support interior rings $this->processLineString($polygon->exteriorRing()); } /** * @param MultiGeometry $collection */ protected function processCollection(MultiGeometry $collection): void { // TODO: multi geometries should be converted to relations foreach ($collection->getComponents() as $component) { $this->processGeometry($component); } } public static function downloadFromOSMByBbox(float $left, float $bottom, float $right, float $top): string { $osmFile = file_get_contents(self::OSM_API_URL . "map?bbox={$left},{$bottom},{$right},{$top}"); if ($osmFile !== false) { return $osmFile; } else { throw new IOException("Failed to download from OSM."); } } } <?php namespace geoPHP\Adapter; use geoPHP\Exception\IOException; use geoPHP\geoPHP; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\GeometryCollection; use geoPHP\Geometry\Point; use geoPHP\Geometry\MultiPoint; use geoPHP\Geometry\LineString; use geoPHP\Geometry\MultiLineString; use geoPHP\Geometry\Polygon; use geoPHP\Geometry\MultiPolygon; use stdClass; /** * GeoJSON class : a geoJSON reader/writer. * * Note that it will always return a GeoJSON geometry. This * means that if you pass it a feature, it will return the * geometry of that feature strip everything else. */ class GeoJSON implements GeoAdapter { /** * Given an object or a string, return a Geometry * * @param string $input The GeoJSON string or object * @return Geometry * @throws IOException */ public function read(string $input): Geometry { $input = json_decode($input); if (!is_object($input)) { throw new IOException('Malformed JSON'); } if (!isset($input->type) || !is_string($input->type)) { throw new IOException('Invalid GeoJSON'); } return $this->processGeoJSON($input); } /** * @param stdClass $input * @return Geometry * @throws IOException */ public function processGeoJSON(stdClass $input): Geometry { // Check to see if it's a FeatureCollection if ($input->type == 'FeatureCollection' && isset($input->features)) { $geometries = []; foreach ($input->features as $feature) { $geometries[] = $this->processGeoJSON($feature); } return geoPHP::buildGeometry($geometries); } // Check to see if it's a Feature if ($input->type == 'Feature') { return $this->geoJSONFeatureToGeometry($input); } // It's a geometry - process it return $this->geoJSONObjectToGeometry($input); } /** * @param stdClass $input * @return int|null */ private function getSRID(stdClass $input): ?int { if (isset($input->crs->properties->name)) { // parse CRS codes in forms "EPSG:1234" and "urn:ogc:def:crs:EPSG::1234" preg_match('#EPSG[:]+(\d+)#', $input->crs->properties->name, $m); return isset($m[1]) ? (int) $m[1] : null; } return null; } /** * @param stdClass $obj * @return Geometry * @throws IOException */ private function geoJSONFeatureToGeometry(stdClass $obj): Geometry { $geometry = $this->processGeoJSON($obj->geometry); if (isset($obj->properties)) { foreach ($obj->properties as $property => $value) { $geometry->setData($property, $value); } } return $geometry; } /** * @param stdClass $obj * @return Geometry * @throws \Exception */ private function geoJSONObjectToGeometry(stdClass $obj): Geometry { $type = $obj->type; if ($type == 'GeometryCollection') { return $this->geoJSONObjectToGeometryCollection($obj); } $method = 'arrayTo' . $type; /** @var GeometryCollection $geometry */ $geometry = $this->$method($obj->coordinates); $geometry->setSRID($this->getSRID($obj)); return $geometry; } /** * @param array<float|int> $coordinates Array of coordinates * @return Point */ private function arrayToPoint(array $coordinates): Point { switch (count($coordinates)) { case 2: return new Point($coordinates[0], $coordinates[1]); case 3: return new Point($coordinates[0], $coordinates[1], $coordinates[2]); case 4: return new Point($coordinates[0], $coordinates[1], $coordinates[2], $coordinates[3]); default: return new Point(); } } /** * @param array<?array<float|int>> $components * @return LineString */ private function arrayToLineString(array $components): LineString { $points = []; foreach ($components as $componentArray) { $points[] = $this->arrayToPoint($componentArray); } return new LineString($points); } /** * @param array<?array<array<float|int>>> $components * @return Polygon */ private function arrayToPolygon(array $components): Polygon { $lines = []; foreach ($components as $componentArray) { $lines[] = $this->arrayToLineString($componentArray); } return new Polygon($lines); } /** * @param array<?array<float|int|null>> $components * @return MultiPoint */ private function arrayToMultiPoint(array $components): MultiPoint { $points = []; foreach ($components as $componentArray) { $points[] = $this->arrayToPoint($componentArray); } return new MultiPoint($points); } /** * @param array<?array<array<float|int|null>>> $components * @return MultiLineString */ private function arrayToMultiLineString(array $components): MultiLineString { $lines = []; foreach ($components as $componentArray) { $lines[] = $this->arrayToLineString($componentArray); } return new MultiLineString($lines); } /** * @param array<?array<array<array<float|int|null>>>> $components * @return MultiPolygon */ private function arrayToMultiPolygon(array $components): MultiPolygon { $polygons = []; foreach ($components as $componentArray) { $polygons[] = $this->arrayToPolygon($componentArray); } return new MultiPolygon($polygons); } /** * @param stdClass $obj * @throws IOException * @return GeometryCollection */ private function geoJSONObjectToGeometryCollection(stdClass $obj): Geometry { $geometries = []; if (!property_exists($obj, 'geometries')) { throw new IOException('Invalid GeoJSON: GeometryCollection without geometry components'); } foreach ($obj->geometries ?: [] as $componentObject) { $geometries[] = $this->geoJSONObjectToGeometry($componentObject); } $collection = new GeometryCollection($geometries); $collection->setSRID($this->getSRID($obj)); return $collection; } /** * Serializes an object into a geojson string * * * @param Geometry $geometry The object to serialize * * @return string The GeoJSON string */ public function write(Geometry $geometry): string { return json_encode($this->geometryToGeoJsonArray($geometry)); } /** * Creates a geoJSON array. * * If the root geometry is a GeometryCollection, and any of its geometries has data, * the root element will be a FeatureCollection with Feature elements (with the data). * If the root geometry has data, it will be included in a Feature object that contains the data. * * The geometry should have geographical coordinates since CRS support has been removed from geoJSON * specification (RFC 7946). * The geometry should'nt be measured, since geoJSON specification (RFC 7946) only supports the dimensional * positions. * * @param Geometry $geometry * @param bool|null $isRoot Is geometry the root geometry? * @return array{type: string, geometries: array<mixed> * }|array{type: string, geometry: array<mixed>, properties: array<mixed> * }|array{type: string, features: array<mixed> * }|array{type: string, coordinates: array<float>} */ public function geometryToGeoJsonArray(Geometry $geometry, ?bool $isRoot = true): array { if ($geometry->geometryType() === Geometry::GEOMETRY_COLLECTION) { $components = []; $isFeatureCollection = false; foreach ($geometry->getComponents() as $component) { if ($component->getData() !== null) { $isFeatureCollection = true; } $components[] = $this->geometryToGeoJsonArray($component, false); } if (!$isFeatureCollection || !$isRoot) { return [ 'type' => 'GeometryCollection', 'geometries' => $components ]; } else { $features = []; foreach ($geometry->getComponents() as $i => $component) { $features[] = [ 'type' => 'Feature', 'properties' => $component->getData(), 'geometry' => $components[$i], ]; } return [ 'type' => 'FeatureCollection', 'features' => $features ]; } } if ($isRoot && $geometry->getData() !== null) { return [ 'type' => 'Feature', 'properties' => $geometry->getData(), 'geometry' => [ 'type' => $geometry->geometryType(), 'coordinates' => $geometry->isEmpty() ? [] : $geometry->asArray() ] ]; } $object = [ 'type' => $geometry->geometryType(), 'coordinates' => $geometry->isEmpty() ? [] : $geometry->asArray() ]; return $object; } } <?php namespace geoPHP\Adapter; use geoPHP\Exception\InvalidGeometryException; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\Point; use geoPHP\Geometry\LineString; use geoPHP\Geometry\Polygon; /** * PHP Geometry GeoHash encoder/decoder. * * @author prinsmc * @see http://en.wikipedia.org/wiki/Geohash * */ class GeoHash implements GeoAdapter { /** * @var string */ public static $characterTable = "0123456789bcdefghjkmnpqrstuvwxyz"; /** * Array of neighbouring hash character maps. * @var array{top: array<string, string>,right: array<string, string>, * left: array<string, string>,bottom: array<string, string> * } */ private static $neighbours = [ // north 'top' => [ 'even' => 'p0r21436x8zb9dcf5h7kjnmqesgutwvy', 'odd' => 'bc01fg45238967deuvhjyznpkmstqrwx' ], // east 'right' => [ 'even' => 'bc01fg45238967deuvhjyznpkmstqrwx', 'odd' => 'p0r21436x8zb9dcf5h7kjnmqesgutwvy' ], // west 'left' => [ 'even' => '238967debc01fg45kmstqrwxuvhjyznp', 'odd' => '14365h7k9dcfesgujnmqp0r2twvyx8zb' ], // south 'bottom' => [ 'even' => '14365h7k9dcfesgujnmqp0r2twvyx8zb', 'odd' => '238967debc01fg45kmstqrwxuvhjyznp' ] ]; /** * array of bordering hash character maps. * @var array{top: array<string, string>,right: array<string, string>, * left: array<string, string>,bottom: array<string, string> * } */ private static $borders = [ // north 'top' => [ 'even' => 'prxz', 'odd' => 'bcfguvyz' ], // east 'right' => [ 'even' => 'bcfguvyz', 'odd' => 'prxz' ], // west 'left' => [ 'even' => '0145hjnp', 'odd' => '028b' ], // south 'bottom' => [ 'even' => '028b', 'odd' => '0145hjnp' ] ]; /** * Convert the geoHash to a Point. The point is 2-dimensional. * * @param string $hash a GeoHash * @param boolean $asGrid Return the center point of hash grid or the grid cell as Polygon * * @return Point|Polygon the converted GeoHash */ public function read(string $hash, bool $asGrid = false): Geometry { $decodedHash = $this->decode($hash); if (!$asGrid) { return new Point($decodedHash['centerLongitude'], $decodedHash['centerLatitude']); } else { return new Polygon( [ new LineString( [ new Point($decodedHash['minLongitude'], $decodedHash['maxLatitude']), new Point($decodedHash['maxLongitude'], $decodedHash['maxLatitude']), new Point($decodedHash['maxLongitude'], $decodedHash['minLatitude']), new Point($decodedHash['minLongitude'], $decodedHash['minLatitude']), new Point($decodedHash['minLongitude'], $decodedHash['maxLatitude']), ] ) ] ); } } /** * Convert the geometry to geohash. * * @param Geometry $geometry * @param float|null $precision * * @return string the GeoHash or null when the $geometry is not a Point */ public function write(Geometry $geometry, float $precision = null): string { if ($geometry->isEmpty()) { return ''; } if ($geometry->geometryType() === Geometry::POINT) { /** @var Point $geometry */ return $this->encodePoint($geometry, $precision); } else { // The GeoHash is the smallest hash grid ID that fits the envelope $envelope = $geometry->envelope(); $geoHashes = []; $geohash = ''; foreach ($envelope->getPoints() as $point) { $geoHashes[] = $this->encodePoint($point, 0.0000001); } $i = 0; while ($i < strlen($geoHashes[0])) { $char = $geoHashes[0][$i]; foreach ($geoHashes as $hash) { if ($hash[$i] != $char) { return $geohash; } } $geohash .= $char; $i++; } return $geohash; } } /** * @author algorithm based on code by Alexander Songe <a@songe.me> * @see https://github.com/asonge/php-geohash/issues/1 * * @param Point $point * @param float|null $precision * @return string The GeoHash * @throws \Exception */ private function encodePoint(Point $point, ?float $precision = null): string { $minLatitude = -90.0000000000001; $maxLatitude = 90.0000000000001; $minLongitude = -180.0000000000001; $maxLongitude = 180.0000000000001; $latitudeError = 90; $longitudeError = 180; $i = 0; $error = 180; $hash = ''; if (!is_numeric($precision)) { $lap = strlen((string) $point->y()) - strpos((string) $point->y(), "."); $lop = strlen((string) $point->x()) - strpos((string) $point->x(), "."); $precision = pow(10, -max($lap - 1, $lop - 1, 0)) / 2; } if ( $point->x() < $minLongitude || $point->y() < $minLatitude || $point->x() > $maxLongitude || $point->y() > $maxLatitude ) { throw new InvalidGeometryException( "Point coordinates ({$point->x()}, {$point->y()}) are out of lat/lon range." ); } while ($error >= $precision) { $chr = 0; for ($b = 4; $b >= 0; --$b) { if ((1 & $b) == (1 & $i)) { // even char, even bit OR odd char, odd bit...a lon $next = ($minLongitude + $maxLongitude) / 2; if ($point->x() > $next) { $chr |= pow(2, $b); $minLongitude = $next; } else { $maxLongitude = $next; } $longitudeError /= 2; } else { // odd char, even bit OR even char, odd bit...a lat $next = ($minLatitude + $maxLatitude) / 2; if ($point->y() > $next) { $chr |= pow(2, $b); $minLatitude = $next; } else { $maxLatitude = $next; } $latitudeError /= 2; } } $hash .= self::$characterTable[$chr]; $i++; $error = min($latitudeError, $longitudeError); } return $hash; } /** * @author algorithm based on code by Alexander Songe <a@songe.me> * @see https://github.com/asonge/php-geohash/issues/1 * * @param string $hash a GeoHash * * @return array<string, float|int> Associative array. */ private function decode(string $hash): array { $result = []; $minLatitude = -90; $maxLatitude = 90; $minLongitude = -180; $maxLongitude = 180; $latitudeError = 90; $longitudeError = 180; for ($i = 0, $c = strlen($hash); $i < $c; $i++) { $v = strpos(self::$characterTable, $hash[$i]); if (1 & $i) { if (16 & $v) { $minLatitude = ($minLatitude + $maxLatitude) / 2; } else { $maxLatitude = ($minLatitude + $maxLatitude) / 2; } if (8 & $v) { $minLongitude = ($minLongitude + $maxLongitude) / 2; } else { $maxLongitude = ($minLongitude + $maxLongitude) / 2; } if (4 & $v) { $minLatitude = ($minLatitude + $maxLatitude) / 2; } else { $maxLatitude = ($minLatitude + $maxLatitude) / 2; } if (2 & $v) { $minLongitude = ($minLongitude + $maxLongitude) / 2; } else { $maxLongitude = ($minLongitude + $maxLongitude) / 2; } if (1 & $v) { $minLatitude = ($minLatitude + $maxLatitude) / 2; } else { $maxLatitude = ($minLatitude + $maxLatitude) / 2; } $latitudeError /= 8; $longitudeError /= 4; } else { if (16 & $v) { $minLongitude = ($minLongitude + $maxLongitude) / 2; } else { $maxLongitude = ($minLongitude + $maxLongitude) / 2; } if (8 & $v) { $minLatitude = ($minLatitude + $maxLatitude) / 2; } else { $maxLatitude = ($minLatitude + $maxLatitude) / 2; } if (4 & $v) { $minLongitude = ($minLongitude + $maxLongitude) / 2; } else { $maxLongitude = ($minLongitude + $maxLongitude) / 2; } if (2 & $v) { $minLatitude = ($minLatitude + $maxLatitude) / 2; } else { $maxLatitude = ($minLatitude + $maxLatitude) / 2; } if (1 & $v) { $minLongitude = ($minLongitude + $maxLongitude) / 2; } else { $maxLongitude = ($minLongitude + $maxLongitude) / 2; } $latitudeError /= 4; $longitudeError /= 8; } } $result['minLatitude'] = $minLatitude; $result['minLongitude'] = $minLongitude; $result['maxLatitude'] = $maxLatitude; $result['maxLongitude'] = $maxLongitude; $result['centerLatitude'] = round( ($minLatitude + $maxLatitude) / 2, max(1, -round(log10($latitudeError))) - 1 ); $result['centerLongitude'] = round( ($minLongitude + $maxLongitude) / 2, max(1, -round(log10($longitudeError))) - 1 ); return $result; } /** * Calculates the adjacent geohash of the geohash in the specified direction. * This algorithm is available in various ports that seem to point back to * geohash-js by David Troy under MIT notice. * * * @see https://github.com/davetroy/geohash-js * @see https://github.com/lyokato/objc-geohash * @see https://github.com/lyokato/libgeohash * @see https://github.com/masuidrive/pr_geohash * @see https://github.com/sunng87/node-geohash * @see https://github.com/davidmoten/geo * * @param string $hash the geohash (lowercase). * @param string $direction the direction of the neighbor (top, bottom, left or right). * * @return string the geohash of the adjacent cell. */ public static function adjacent(string $hash, string $direction): string { $last = substr($hash, -1); $type = (strlen($hash) % 2) ? 'odd' : 'even'; $base = substr($hash, 0, strlen($hash) - 1); if (strpos((self::$borders[$direction][$type]), $last) !== false) { $base = self::adjacent($base, $direction); } return $base . self::$characterTable[strpos(self::$neighbours[$direction][$type], $last)]; } } <?php namespace geoPHP\Adapter; /** * Class GpxTypes * Defines the available GPX types and their allowed elements following the GPX specification * * @see http://www.topografix.com/gpx/1/1/ * @package geoPHP\Adapter */ class GpxTypes { /** * @var string[] Allowed elements in <gpx> * @see http://www.topografix.com/gpx/1/1/#type_gpxType */ public static $gpxTypeElements = [ 'metadata', 'wpt', 'rte', 'trk' ]; /** * @var string[] Allowed elements in <trk> * @see http://www.topografix.com/gpx/1/1/#type_trkType */ public static $trkTypeElements = [ 'name', 'cmt', 'desc', 'src', 'link', 'number', 'type' ]; /** * same as trkTypeElements * @var string[] Allowed elements in <rte> * @see http://www.topografix.com/gpx/1/1/#type_rteType */ public static $rteTypeElements = [ 'name', 'cmt', 'desc', 'src', 'link', 'number', 'type' ]; /** * @var string[] Allowed elements in <wpt> * @see http://www.topografix.com/gpx/1/1/#type_wptType */ public static $wptTypeElements = [ 'ele', 'time', 'magvar', 'geoidheight', 'name', 'cmt', 'desc', 'src', 'link', 'sym', 'type', 'fix', 'sat', 'hdop', 'vdop', 'pdop', 'ageofdgpsdata', 'dgpsid' ]; /** * @var string[] Same as wptType */ public static $trkptTypeElements = [ // same as wptTypeElements 'ele', 'time', 'magvar', 'geoidheight', 'name', 'cmt', 'desc', 'src', 'link', 'sym', 'type', 'fix', 'sat', 'hdop', 'vdop', 'pdop', 'ageofdgpsdata', 'dgpsid' ]; /** * @var string[] Same as wptType */ public static $rteptTypeElements = [ // same as wptTypeElements 'ele', 'time', 'magvar', 'geoidheight', 'name', 'cmt', 'desc', 'src', 'link', 'sym', 'type', 'fix', 'sat', 'hdop', 'vdop', 'pdop', 'ageofdgpsdata', 'dgpsid' ]; /** * @var string[] Allowed elements in <metadata> * @see http://www.topografix.com/gpx/1/1/#type_metadataType */ public static $metadataTypeElements = [ 'name', 'desc', 'author', 'copyright', 'link', 'time', 'keywords', 'bounds' ]; /** * @var string[] Allowed elements in <gpx> * @see http://www.topografix.com/gpx/1/1/#type_gpxType */ protected $allowedGpxTypeElements; /** * @var string[] Allowed elements in <trk> * @see http://www.topografix.com/gpx/1/1/#type_trkType */ protected $allowedTrkTypeElements; /** * same as trkTypeElements * @var string[] Allowed elements in <rte> * @see http://www.topografix.com/gpx/1/1/#type_rteType */ protected $allowedRteTypeElements = []; /** * @var string[] Same as wptType */ protected $allowedWptTypeElements = []; /** * @var string[] Same as wptType */ protected $allowedTrkptTypeElements = []; /** * @var string[] Same as wptType */ protected $allowedRteptTypeElements = []; /** * @var string[] Allowed elements in <metadata> * @see http://www.topografix.com/gpx/1/1/#type_metadataType */ protected $allowedMetadataTypeElements = []; /** * GpxTypes constructor. * * @param array<string, ?array<string>>|null $allowedElements Which elements can be used in each GPX type. * If not specified, every element defined in the GPX specification can be used. * Can be overwritten with an associative array, with type name in keys. * eg.: ['wptType' => ['ele', 'name'], 'trkptType' => ['ele'], 'metadataType' => null] */ public function __construct(?array $allowedElements = null) { $this->allowedGpxTypeElements = self::$gpxTypeElements; $this->allowedTrkTypeElements = self::$trkTypeElements; $this->allowedRteTypeElements = self::$rteTypeElements; $this->allowedWptTypeElements = self::$wptTypeElements; $this->allowedTrkptTypeElements = self::$trkTypeElements; $this->allowedRteptTypeElements = self::$rteptTypeElements; $this->allowedMetadataTypeElements = self::$metadataTypeElements; foreach ($allowedElements ?: [] as $type => $elements) { $elements = is_array($elements) ? $elements : [$elements]; $this->{'allowed' . ucfirst($type) . 'Elements'} = []; foreach ($this::${$type . 'Elements'} as $availableType) { if (in_array($availableType, $elements)) { $this->{'allowed' . ucfirst($type) . 'Elements'}[] = $availableType; } } } } /** * Returns an array of allowed elements for the given GPX type * eg. "gpxType" returns ['metadata', 'wpt', 'rte', 'trk'] * * @param string $type One of the following GPX types: * gpxType, trkType, rteType, wptType, trkptType, rteptType, metadataType * @return string[] */ public function get(string $type): array { $propertyName = 'allowed' . ucfirst($type) . 'Elements'; if (isset($this->{$propertyName})) { return $this->{$propertyName}; } return []; } } <?php namespace geoPHP\Adapter; use geoPHP\Exception\IOException; use geoPHP\Geometry\Geometry; /** * EWKT (Extended Well Known Text) Adapter */ class EWKT extends WKT { /** * Serialize geometries into an EWKT string. * * @param Geometry $geometry * * @throws IOException Throwed if the given Geometry is not supported by the EWKT writer. * * @return string The Extended-WKT string representation of the input geometries. */ public function write(Geometry $geometry): string { $srid = $geometry->getSRID(); if ($srid) { return 'SRID=' . $srid . ';' . $geometry->out('wkt'); } else { return $geometry->out('wkt'); } } } <?php /** * This file contains the BinaryReader class. * For more information see the class description below. * * @author Peter Bathory <peter.bathory@cartographia.hu> * @since 2016-02-18 * * This code is open-source and licenced under the Modified BSD License. * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace geoPHP\Adapter; use geoPHP\Exception\InvalidGeometryException; use geoPHP\Exception\IOException; use geoPHP\Geometry\Collection; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\GeometryCollection; use geoPHP\Geometry\LineString; use geoPHP\Geometry\MultiGeometry; use geoPHP\Geometry\MultiLineString; use geoPHP\Geometry\MultiPoint; use geoPHP\Geometry\MultiPolygon; use geoPHP\Geometry\Point; use geoPHP\Geometry\Polygon; /** * PHP Geometry <-> TWKB encoder/decoder * * "Tiny Well-known Binary is a multi-purpose format for serializing vector geometry data into a byte buffer, * with an emphasis on minimizing size of the buffer." * @see https://github.com/TWKB/Specification/blob/master/twkb.md * * This implementation supports: * - reading and writing all geometry types (1-7) * - empty geometries * - extended precision (Z, M coordinates; custom precision) * Partially supports: * - bounding box: can read and write, but don't store readed boxes (API missing) * - size attribute: can read and write size attribute, but seeking is not supported * - ID list: can read and write, but API is completely missing */ class TWKB implements GeoAdapter { /** * @var array<string, mixed> */ protected $writeOptions = [ 'decimalDigitsXY' => 5, 'decimalDigitsZ' => 0, 'decimalDigitsM' => 0, 'includeSize' => false, 'includeBoundingBoxes' => false, 'xyFactor' => null, 'zFactor' => null, 'mFactor' => null, 'hasZ' => null, 'hasM' => null, ]; /** * @var Point|null */ private $lastPoint = null; /** * @var BinaryReader */ private $reader; /** * @var BinaryWriter */ private $writer; /** * @var array<string, int> Maps Geometry types to TWKB type codes */ protected static $typeMap = [ Geometry::POINT => 1, Geometry::LINE_STRING => 2, Geometry::POLYGON => 3, Geometry::MULTI_POINT => 4, Geometry::MULTI_LINE_STRING => 5, Geometry::MULTI_POLYGON => 6, Geometry::GEOMETRY_COLLECTION => 7 ]; /** * Read TWKB into geometry objects * * @param string $twkb Tiny Well-known-binary string * @param bool $isHexString If this is a hexadecimal string that is in need of packing * * @return Geometry * * @throws \Exception * @throws IOException */ public function read(string $twkb, bool $isHexString = false): Geometry { if ($isHexString) { $twkb = @pack('H*', $twkb); } if (empty($twkb)) { throw new IOException('Cannot read empty TWKB input. Found ' . gettype($twkb)); } $this->reader = new BinaryReader($twkb); $geometry = $this->getGeometry(); $this->reader->close(); return $geometry; } protected function getGeometry(): Geometry { $options = []; $type = $this->reader->readUInt8(); $metadataHeader = $this->reader->readUInt8(); $geometryType = $type & 0x0F; $options['precision'] = BinaryReader::zigZagDecode($type >> 4); $options['precisionFactor'] = pow(10, $options['precision']); $options['hasBoundingBox'] = ($metadataHeader >> 0 & 1) == 1; $options['hasSizeAttribute'] = ($metadataHeader >> 1 & 1) == 1; $options['hasIdList'] = ($metadataHeader >> 2 & 1) == 1; $options['hasExtendedPrecision'] = ($metadataHeader >> 3 & 1) == 1; $options['isEmpty'] = ($metadataHeader >> 4 & 1) == 1; $options['unused1'] = ($metadataHeader >> 5 & 1) == 1; $options['unused2'] = ($metadataHeader >> 6 & 1) == 1; $options['unused3'] = ($metadataHeader >> 7 & 1) == 1; if ($options['hasExtendedPrecision']) { $extendedPrecision = $this->reader->readUInt8(); $options['hasZ'] = ($extendedPrecision & 0x01) === 0x01; $options['hasM'] = ($extendedPrecision & 0x02) === 0x02; $options['zPrecision'] = ($extendedPrecision & 0x1C) >> 2; $options['zPrecisionFactor'] = pow(10, $options['zPrecision']); $options['mPrecision'] = ($extendedPrecision & 0xE0) >> 5; $options['mPrecisionFactor'] = pow(10, $options['mPrecision']); } else { $options['hasZ'] = false; $options['hasM'] = false; } if ($options['hasSizeAttribute']) { $options['remainderSize'] = $this->reader->readUVarInt(); } if ($options['hasBoundingBox']) { $dimension = 2 + ($options['hasZ'] ? 1 : 0) + ($options['hasM'] ? 1 : 0); $precisions = [ $options['precisionFactor'], $options['precisionFactor'], $options['hasZ'] ? $options['zPrecisionFactor'] : 0, $options['hasM'] ? $options['mPrecisionFactor'] : 0 ]; $bBoxMin = $bBoxMax = []; for ($i = 0; $i < $dimension; $i++) { $bBoxMin[$i] = $this->reader->readUVarInt() / $precisions[$i]; $bBoxMax[$i] = $this->reader->readUVarInt() / $precisions[$i] + $bBoxMin[$i]; } /** @noinspection PhpUndefinedVariableInspection (minimum 2 dimension) */ $options['boundingBox'] = ['minXYZM' => $bBoxMin, 'maxXYZM' => $bBoxMax]; } if ($options['unused1']) { $this->reader->readUVarInt(); } if ($options['unused2']) { $this->reader->readUVarInt(); } if ($options['unused3']) { $this->reader->readUVarInt(); } $this->lastPoint = new Point(0, 0, 0, 0); switch ($geometryType) { case 1: $geometry = $this->getPoint($options); break; case 2: $geometry = $this->getLineString($options); break; case 3: $geometry = $this->getPolygon($options); break; case 4: $geometry = $this->getMulti('Point', $options); break; case 5: $geometry = $this->getMulti('LineString', $options); break; case 6: $geometry = $this->getMulti('Polygon', $options); break; case 7: $geometry = $this->getMulti('Geometry', $options); break; default: throw new IOException( 'Geometry type ' . $geometryType . ' (' . (array_search($geometryType, self::$typeMap) ?: 'unknown') . ') not supported' ); } return $geometry; } /** * @param array<string, mixed> $options * * @return Point * @throws InvalidGeometryException */ protected function getPoint(array $options): Point { if ($options['isEmpty']) { return new Point(); } $x = round( $this->lastPoint->x() + $this->reader->readSVarInt() / $options['precisionFactor'], $options['precision'] ); $y = round( $this->lastPoint->y() + $this->reader->readSVarInt() / $options['precisionFactor'], $options['precision'] ); $z = $options['hasZ'] ? round( $this->lastPoint->z() + $this->reader->readSVarInt() / $options['zPrecisionFactor'], $options['zPrecision'] ) : null; $m = $options['hasM'] ? round( $this->lastPoint->m() + $this->reader->readSVarInt() / $options['mPrecisionFactor'], $options['mPrecision'] ) : null; $this->lastPoint = new Point($x, $y, $z, $m); return $this->lastPoint; } /** * @param array<string, mixed> $options * * @return LineString * @throws InvalidGeometryException */ protected function getLineString(array $options): LineString { if ($options['isEmpty']) { return new LineString(); } $pointCount = $this->reader->readUVarInt(); $points = []; for ($i = 0; $i < $pointCount; $i++) { $points[] = $this->getPoint($options); } return new LineString($points); } /** * @param array<string, mixed> $options * * @return Polygon * @throws InvalidGeometryException */ protected function getPolygon(array $options): Polygon { if ($options['isEmpty']) { return new Polygon(); } $ringCount = $this->reader->readUVarInt(); $rings = []; for ($i = 0; $i < $ringCount; $i++) { $rings[] = $this->getLineString($options); } return new Polygon($rings, true); } /** * @param string $type * @param array<string, mixed> $options * * @return MultiGeometry * @throws InvalidGeometryException */ protected function getMulti(string $type, array $options): MultiGeometry { $multiLength = $this->reader->readUVarInt(); if ($options['hasIdList']) { for ($i = 0; $i < $multiLength; $i++) { $idList[] = $this->reader->readSVarInt(); } } $components = []; for ($i = 0; $i < $multiLength; $i++) { if ($type !== 'Geometry') { $func = 'get' . $type; $components[] = $this->$func($options); } else { $components[] = $this->getGeometry(); } } switch ($type) { case 'Point': return new MultiPoint($components); case 'LineString': return new MultiLineString($components); case 'Polygon': return new MultiPolygon($components); case 'Geometry': return new GeometryCollection($components); } throw new IOException("Unknown multi-geometry type '$type'."); } /******* WRITER *******/ /** * Serialize geometries into TWKB string. * * @param Geometry $geometry The geometry * @param bool $writeAsHex Write the result in binary or hexadecimal system. Default is false. * @param int|null $decimalDigitsXY Coordinate precision of X and Y. Default is 5 decimals. * @param null $decimalDigitsZ Coordinate precision of Z. Default is 0 decimal. * @param null $decimalDigitsM Coordinate precision of M. Default is 0 decimal. * @param bool $includeSizes Includes size in bytes of the remainder geometry after the size attribute. * Default is false. * @param bool $includeBoundingBoxes Includes the coordinates of bounding box' two corner. Default is false. * * @return string binary or hexadecimal representation of TWKB. */ public function write( Geometry $geometry, bool $writeAsHex = false, ?int $decimalDigitsXY = 5, ?int $decimalDigitsZ = 0, ?int $decimalDigitsM = 0, ?bool $includeSizes = false, ?bool $includeBoundingBoxes = false ): string { $this->writer = new BinaryWriter(); $this->writeOptions = [ 'decimalDigitsXY' => $decimalDigitsXY !== null ? $decimalDigitsXY : $this->writeOptions['decimalDigitsXY'], 'decimalDigitsZ' => $decimalDigitsZ !== null ? $decimalDigitsZ : $this->writeOptions['decimalDigitsZ'], 'decimalDigitsM' => $decimalDigitsM !== null ? $decimalDigitsM : $this->writeOptions['decimalDigitsM'], 'includeSize' => $includeSizes != null ? $includeSizes : $this->writeOptions['includeSize'], 'includeBoundingBoxes' => $includeBoundingBoxes !== null ? $includeBoundingBoxes : $this->writeOptions['includeBoundingBoxes'] ]; $this->writeOptions = array_merge( $this->writeOptions, [ 'xyFactor' => pow(10, $this->writeOptions['decimalDigitsXY']), 'zFactor' => pow(10, $this->writeOptions['decimalDigitsZ']), 'mFactor' => pow(10, $this->writeOptions['decimalDigitsM']) ] ); $twkb = $this->writeGeometry($geometry); return $writeAsHex ? current(unpack('H*', $twkb)) : $twkb; } /** * @param Geometry $geometry * * @return string */ protected function writeGeometry(Geometry $geometry): string { $this->writeOptions['hasZ'] = $geometry->is3D(); $this->writeOptions['hasM'] = $geometry->isMeasured(); // Type and precision $type = self::$typeMap[$geometry->geometryType()] + (BinaryWriter::zigZagEncode($this->writeOptions['decimalDigitsXY']) << 4); $twkbHead = $this->writer->writeUInt8($type); // Is there extended precision information? $metadataHeader = $this->writeOptions['includeBoundingBoxes'] << 0; // Is there extended precision information? $metadataHeader += $this->writeOptions['includeSize'] << 1; // Is there an ID list? // TODO: implement this (needs metadata support in geoPHP) //$metadataHeader += $this->writeOptions['hasIdList'] << 2; // Is there extended precision information? $metadataHeader += ($geometry->is3D() || $geometry->isMeasured()) << 3; // Is this an empty geometry? $metadataHeader += $geometry->isEmpty() << 4; $twkbHead .= $this->writer->writeUInt8($metadataHeader); $twkbGeom = ''; if (!$geometry->isEmpty()) { $this->lastPoint = new Point(0, 0, 0, 0); switch ($geometry->geometryType()) { case Geometry::POINT: /** @var Point $geometry */ $twkbGeom .= $this->writePoint($geometry); break; case Geometry::LINE_STRING: /** @var LineString $geometry */ $twkbGeom .= $this->writeLineString($geometry); break; case Geometry::POLYGON: /** @var Polygon $geometry */ $twkbGeom .= $this->writePolygon($geometry); break; case Geometry::MULTI_POINT: case Geometry::MULTI_LINE_STRING: case Geometry::MULTI_POLYGON: case Geometry::GEOMETRY_COLLECTION: /** @var MultiGeometry $geometry */ $twkbGeom .= $this->writeMulti($geometry); break; } } if ($this->writeOptions['includeBoundingBoxes']) { $bBox = $geometry->getBBox(); // X $twkbBox = $this->writer->writeSVarInt($bBox['minx'] * $this->writeOptions['xyFactor']); $twkbBox .= $this->writer->writeSVarInt(($bBox['maxx'] - $bBox['minx']) * $this->writeOptions['xyFactor']); // Y $twkbBox .= $this->writer->writeSVarInt($bBox['miny'] * $this->writeOptions['xyFactor']); $twkbBox .= $this->writer->writeSVarInt(($bBox['maxy'] - $bBox['miny']) * $this->writeOptions['xyFactor']); if ($geometry->is3D()) { $bBox['minz'] = $geometry->minimumZ(); $bBox['maxz'] = $geometry->maximumZ(); $twkbBox .= $this->writer->writeSVarInt( round($bBox['minz'] * $this->writeOptions['zFactor']) ); $twkbBox .= $this->writer->writeSVarInt( round(($bBox['maxz'] - $bBox['minz']) * $this->writeOptions['zFactor']) ); } if ($geometry->isMeasured()) { $bBox['minm'] = $geometry->minimumM(); $bBox['maxm'] = $geometry->maximumM(); $twkbBox .= $this->writer->writeSVarInt($bBox['minm'] * $this->writeOptions['mFactor']); $twkbBox .= $this->writer->writeSVarInt( ($bBox['maxm'] - $bBox['minm']) * $this->writeOptions['mFactor'] ); } $twkbGeom = $twkbBox . $twkbGeom; } if ($geometry->is3D() || $geometry->isMeasured()) { $extendedPrecision = 0; if ($geometry->is3D()) { $extendedPrecision |= 0x1 | ($this->writeOptions['decimalDigitsZ'] << 2); } if ($geometry->isMeasured()) { $extendedPrecision |= 0x2 | ($this->writeOptions['decimalDigitsM'] << 5); } $twkbHead .= $this->writer->writeUInt8($extendedPrecision); } if ($this->writeOptions['includeSize']) { $twkbHead .= $this->writer->writeUVarInt(strlen($twkbGeom)); } return $twkbHead . $twkbGeom; } /** * @param Point $geometry * @return string */ protected function writePoint(Point $geometry): string { $x = round($geometry->x() * $this->writeOptions['xyFactor']); $y = round($geometry->y() * $this->writeOptions['xyFactor']); $z = round($geometry->z() * $this->writeOptions['zFactor']); $m = round($geometry->m() * $this->writeOptions['mFactor']); $twkb = $this->writer->writeSVarInt($x - $this->lastPoint->x()); $twkb .= $this->writer->writeSVarInt($y - $this->lastPoint->y()); if ($this->writeOptions['hasZ']) { $twkb .= $this->writer->writeSVarInt($z - $this->lastPoint->z()); } if ($this->writeOptions['hasM']) { $twkb .= $this->writer->writeSVarInt($m - $this->lastPoint->m()); } $this->lastPoint = new Point( $x, $y, $this->writeOptions['hasZ'] ? $z : null, $this->writeOptions['hasM'] ? $m : null ); return $twkb; } /** * @param LineString $geometry * @return string */ protected function writeLineString(LineString $geometry): string { $twkb = $this->writer->writeUVarInt($geometry->numPoints()); foreach ($geometry->getComponents() as $component) { $twkb .= $this->writePoint($component); } return $twkb; } /** * @param Polygon $geometry * @return string */ protected function writePolygon(Polygon $geometry): string { $twkb = $this->writer->writeUVarInt($geometry->numGeometries()); foreach ($geometry->getComponents() as $component) { $twkb .= $this->writeLineString($component); } return $twkb; } /** * @param MultiGeometry $geometry * @return string */ protected function writeMulti(MultiGeometry $geometry): string { $twkb = $this->writer->writeUVarInt($geometry->numGeometries()); //if ($geometry->hasIdList()) { // foreach ($geometry->getComponents() as $component) { // $this->writer->writeUVarInt($component->getId()); // } //} foreach ($geometry->getComponents() as $component) { if ($geometry->geometryType() !== Geometry::GEOMETRY_COLLECTION) { $func = 'write' . $component->geometryType(); $twkb .= $this->$func($component); } else { $twkb .= $this->writeGeometry($component); } } return $twkb; } } <?php namespace geoPHP\Adapter; use DOMNode; use geoPHP\Exception\FileFormatException; use geoPHP\Exception\InvalidXmlException; use geoPHP\geoPHP; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\GeometryCollection; use geoPHP\Geometry\Point; use geoPHP\Geometry\LineString; use geoPHP\Geometry\MultiGeometry; use geoPHP\Geometry\Polygon; /* * Copyright (c) Patrick Hayes * Copyright (c) 2010-2011, Arnaud Renevier * * This code is open-source and licenced under the Modified BSD License. * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * PHP Geometry/KML encoder/decoder * * Mainly inspired/adapted from OpenLayers( http://www.openlayers.org ) * Openlayers/format/WKT.js * * @package sfMapFishPlugin * @subpackage GeoJSON * @author Camptocamp <info@camptocamp.com> */ class KML implements GeoAdapter { /** * @var \DOMDocument */ protected $xmlObject; /** * @var string */ private $nss = ''; // Name-space string. eg 'georss:' /** * Read KML string into geometry objects * * @param string $kml A KML string * * @return Geometry|GeometryCollection * @throws InvalidXmlException If KML is not a valid XML */ public function read(string $kml): Geometry { // Change to lower-case and strip all CDATA $kml = mb_strtolower($kml, mb_detect_encoding($kml)); $kml = preg_replace('/<!\[cdata\[(.*?)\]\]>/s', '', $kml); // Load into DOMDocument $this->xmlObject = new \DOMDocument(); $loadSuccess = @$this->xmlObject->loadXML($kml); if (!$loadSuccess) { throw new InvalidXmlException(); } // TODO refactor try { $geom = $this->geomFromXML(); } catch (\Exception $e) { throw new FileFormatException("Cannot Read Geometry From KML. " . $e->getMessage()); } return $geom; } protected function geomFromXML(): Geometry { $geometries = []; $placemarkElements = $this->xmlObject->getElementsByTagName('placemark'); if ($placemarkElements->length) { foreach ($placemarkElements as $placemark) { $data = []; /** @var Geometry|null $geometry */ $geometry = null; foreach ($placemark->childNodes as $child) { // Node names are all the same, except for MultiGeometry, which maps to GeometryCollection $nodeName = $child->nodeName == 'multigeometry' ? 'geometrycollection' : $child->nodeName; if (array_key_exists($nodeName, geoPHP::getGeometryList())) { $function = 'parse' . geoPHP::getGeometryList()[$nodeName]; $geometry = $this->$function($child); } elseif ($child->nodeType === 1) { $data[$child->nodeName] = $child->nodeValue; } } if ($geometry) { if (count($data)) { $geometry->setData($data); } $geometries[] = $geometry; } } return new GeometryCollection($geometries); } else { // The document does not have a placemark, try to create a valid geometry from the root element $nodeName = $this->xmlObject->documentElement->nodeName == 'multigeometry' ? 'geometrycollection' : $this->xmlObject->documentElement->nodeName; if (array_key_exists($nodeName, geoPHP::getGeometryList())) { $function = 'parse' . geoPHP::getGeometryList()[$nodeName]; return $this->$function($this->xmlObject->documentElement); } } //return geoPHP::geometryReduce($geometries); return new GeometryCollection(); } /** * @param ?DOMNode $xml * @param string $nodeName * * @return DOMNode[] */ protected function childElements(?DOMNode $xml, string $nodeName = ''): array { $children = []; if ($xml) { foreach ($xml->childNodes as $child) { if ($child->nodeName == $nodeName) { $children[] = $child; } } } return $children; } protected function parsePoint(DOMNode $xml): Point { $coordinates = $this->extractCoordinates($xml); if (empty($coordinates)) { return new Point(); } return new Point( $coordinates[0][0], $coordinates[0][1], ($coordinates[0][2] ?? null), ($coordinates[0][3] ?? null) ); } protected function parseLineString(DOMNode $xml): LineString { $coordinates = $this->extractCoordinates($xml); $pointArray = []; $hasZ = false; $hasM = false; foreach ($coordinates as $set) { $hasZ = $hasZ || (isset($set[2]) && $set[2]); $hasM = $hasM || (isset($set[3]) && $set[3]); } foreach ($coordinates as $set) { $pointArray[] = new Point( $set[0], $set[1], ($hasZ ? ($set[2] ?? 0) : null), ($hasM ? ($set[3] ?? 0) : null) ); } return new LineString($pointArray); } protected function parsePolygon(DOMNode $xml): Polygon { $components = []; /** @noinspection SpellCheckingInspection */ $outerBoundaryIs = $this->childElements($xml, 'outerboundaryis'); if (!$outerBoundaryIs) { return new Polygon(); } $outerBoundaryElement = $outerBoundaryIs[0]; /** @noinspection SpellCheckingInspection */ $outerRingElement = @$this->childElements($outerBoundaryElement, 'linearring')[0]; $components[] = $this->parseLineString($outerRingElement); /** @noinspection SpellCheckingInspection */ $innerBoundaryElementIs = $this->childElements($xml, 'innerboundaryis'); foreach ($innerBoundaryElementIs as $innerBoundaryElement) { /** @noinspection SpellCheckingInspection */ foreach ($this->childElements($innerBoundaryElement, 'linearring') as $innerRingElement) { $components[] = $this->parseLineString($innerRingElement); } } return new Polygon($components); } protected function parseGeometryCollection(DOMNode $xml): GeometryCollection { $components = []; $geometryTypes = geoPHP::getGeometryList(); foreach ($xml->childNodes as $child) { /** @noinspection SpellCheckingInspection */ $nodeName = ($child->nodeName == 'linearring') ? 'linestring' : ($child->nodeName == 'multigeometry' ? 'geometrycollection' : $child->nodeName); if (array_key_exists($nodeName, $geometryTypes)) { $function = 'parse' . $geometryTypes[$nodeName]; $components[] = $this->$function($child); } } return new GeometryCollection($components); } /** * @param DOMNode $xml * @return array<array<string>> */ protected function extractCoordinates(DOMNode $xml): array { $coordinateElements = $this->childElements($xml, 'coordinates'); $coordinates = []; if (!empty($coordinateElements)) { $coordinateSets = explode(' ', preg_replace('/[\r\n\s\t]+/', ' ', $coordinateElements[0]->nodeValue)); foreach ($coordinateSets as $setString) { $setString = trim($setString); if ($setString) { $setArray = explode(',', $setString); if (count($setArray) >= 2) { $coordinates[] = $setArray; } } } } return $coordinates; } /** * Serialize geometries into a KML string. * * @param Geometry $geometry * @param null|string $namespace * @return string The KML string representation of the input geometries */ public function write(Geometry $geometry, $namespace = null): string { if ($namespace) { $this->nss = $namespace . ':'; } return $this->geometryToKML($geometry); } /** * @param Geometry $geometry * @return string */ private function geometryToKML(Geometry $geometry): string { $type = $geometry->geometryType(); switch ($type) { case Geometry::POINT: /** @var Point $geometry */ return $this->pointToKML($geometry); case Geometry::LINE_STRING: /** @var LineString $geometry */ return $this->linestringToKML($geometry); case Geometry::POLYGON: /** @var Polygon $geometry */ return $this->polygonToKML($geometry); case Geometry::MULTI_POINT: case Geometry::MULTI_LINE_STRING: case Geometry::MULTI_POLYGON: case Geometry::GEOMETRY_COLLECTION: /** @var MultiGeometry $geometry */ return $this->collectionToKML($geometry); } return ''; } /** * @param Point $geometry * @return string */ private function pointToKML(Point $geometry): string { $str = '<' . $this->nss . "Point>\n<" . $this->nss . 'coordinates>'; if ($geometry->isEmpty()) { $str .= "0,0"; } else { $str .= $geometry->x() . ',' . $geometry->y() . ($geometry->is3D() ? ',' . $geometry->z() : ''); } return $str . '</' . $this->nss . 'coordinates></' . $this->nss . "Point>\n"; } /** * @param LineString $geometry * @param string|null $type * @return string */ private function linestringToKML(LineString $geometry, string $type = null): string { if (!$type) { $type = $geometry->geometryType(); } $str = '<' . $this->nss . $type . ">\n"; if (!$geometry->isEmpty()) { $str .= '<' . $this->nss . 'coordinates>'; $i = 0; foreach ($geometry->getComponents() as $comp) { if ($i != 0) { $str .= ' '; } $str .= $comp->x() . ',' . $comp->y(); $i++; } $str .= '</' . $this->nss . 'coordinates>'; } $str .= '</' . $this->nss . $type . ">\n"; return $str; } /** * @param Polygon $geometry * @return string */ public function polygonToKML(Polygon $geometry): string { $components = $geometry->getComponents(); $str = ''; if (!empty($components)) { /** @noinspection PhpParamsInspection */ $str = '<' . $this->nss . 'outerBoundaryIs>' . $this->linestringToKML($components[0], 'LinearRing') . '</' . $this->nss . 'outerBoundaryIs>'; foreach (array_slice($components, 1) as $comp) { $str .= '<' . $this->nss . 'innerBoundaryIs>' . $this->linestringToKML($comp) . '</' . $this->nss . 'innerBoundaryIs>'; } } return '<' . $this->nss . "Polygon>\n" . $str . '</' . $this->nss . "Polygon>\n"; } /** * @param MultiGeometry $geometry * @return string */ public function collectionToKML(MultiGeometry $geometry): string { $components = $geometry->getComponents(); $str = '<' . $this->nss . "MultiGeometry>\n"; foreach ($components as $component) { $subAdapter = new KML(); $str .= $subAdapter->write($component); } return $str . '</' . $this->nss . "MultiGeometry>\n"; } } <?php namespace geoPHP\Adapter; use DOMDocument; use DOMElement; use DOMNode; use DOMXPath; use geoPHP\Exception\FileFormatException; use geoPHP\Exception\InvalidXmlException; use geoPHP\Geometry\Collection; use geoPHP\geoPHP; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\GeometryCollection; use geoPHP\Geometry\Point; use geoPHP\Geometry\LineString; use geoPHP\Geometry\MultiLineString; /* * Copyright (c) Patrick Hayes * * This code is open-source and licenced under the Modified BSD License. * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * PHP Geometry/GPX encoder/decoder */ class GPX implements GeoAdapter { /** @var string $nss Name-space string. eg 'georss:' */ protected $nss = ''; /** * @var GpxTypes */ protected $gpxTypes; /** * @var DOMXPath */ protected $xpath; /** * @var boolean */ protected $parseGarminRpt = false; /** * @var Point[]|null */ protected $trackFromRoute = null; /** * Read GPX string into geometry object * * @param string $gpx A GPX string * @param array<string, array<mixed>>|null $allowedElements Which elements can be read from each GPX type. * If not specified, every element defined in the GPX specification can be read * Can be overwritten with an associative array, with type name in keys. * eg.: ['wptType' => ['ele', 'name'], 'trkptType' => ['ele'], 'metadataType' => null] * @return Geometry * @throws InvalidXmlException If GPX is not a valid XML * @throws FileFormatException If cannot parse GPX */ public function read(string $gpx, ?array $allowedElements = null): Geometry { $this->gpxTypes = new GpxTypes($allowedElements); //libxml_use_internal_errors(true); // why? // Load into DOMDocument $xmlObject = new DOMDocument(); $xmlObject->preserveWhiteSpace = false; $loadSuccess = @$xmlObject->loadXML($gpx); if (!$loadSuccess) { throw new InvalidXmlException(); } $this->parseGarminRpt = strpos($gpx, 'gpxx:rpt') > 0; // Initialize XPath parser if needed (currently only for Garmin extensions) if ($this->parseGarminRpt) { $this->xpath = new DOMXPath($xmlObject); $this->xpath->registerNamespace('gpx', 'http://www.topografix.com/GPX/1/1'); $this->xpath->registerNamespace('gpxx', 'http://www.garmin.com/xmlschemas/GpxExtensions/v3'); } try { $geom = $this->geomFromXML($xmlObject); if ($geom->isEmpty()) { /* Geometry was empty but maybe because its tags was not lower cased. We try to lower-case tags and try to run again, but just once. */ $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); $caller = $backtrace[1]['function'] ?? null; if ($caller && $caller !== __FUNCTION__) { $gpx = preg_replace_callback( "/(<\/?\w+)(.*?>)/", function ($m) { return strtolower($m[1]) . $m[2]; }, $gpx ); $geom = $this->read($gpx, $allowedElements); } } } catch (\Exception $e) { throw new FileFormatException("Cannot Read Geometry From GPX. " . $e->getMessage(), null, 0, $e); } return $geom; } /** * Parses the GPX XML and returns a geometry * @param DOMDocument $xmlObject * @return Geometry Returns the geometry representation of the GPX (@see geoPHP::buildGeometry) */ protected function geomFromXML(DOMDocument $xmlObject): Geometry { /** @var Geometry[] $geometries */ $geometries = array_merge( $this->parseWaypoints($xmlObject), $this->parseTracks($xmlObject), $this->parseRoutes($xmlObject) ); if (isset($this->trackFromRoute)) { $trackFromRouteGeom = new LineString($this->trackFromRoute); $trackFromRouteGeom->setData('gpxType', 'track'); $trackFromRouteGeom->setData('type', 'planned route'); $geometries[] = $trackFromRouteGeom; } $geometry = geoPHP::buildGeometry($geometries); if ( in_array('metadata', $this->gpxTypes->get('gpxType')) && $xmlObject->getElementsByTagName('metadata')->length === 1 ) { $metadata = self::parseNodeProperties( $xmlObject->getElementsByTagName('metadata')->item(0), $this->gpxTypes->get('metadataType') ); if ($geometry->getData() !== null && $metadata !== null) { $geometry = new GeometryCollection([$geometry]); } $geometry->setData($metadata); } return $geometry; } /** * @param DOMNode $xml * @param string $nodeName * @return DOMNode[] */ protected function childElements(DOMNode $xml, string $nodeName = ''): array { $children = []; foreach ($xml->childNodes as $child) { if ($child->nodeName == $nodeName) { $children[] = $child; } } return $children; } /** * @param DOMElement $node * @return Point */ protected function parsePoint(DOMElement $node): Point { $lat = (float) $node->attributes->getNamedItem("lat")->nodeValue; $lon = (float) $node->attributes->getNamedItem("lon")->nodeValue; $elevation = null; $ele = $node->getElementsByTagName('ele'); if ($ele->length) { $elevation = (float) $ele->item(0)->nodeValue; } $point = new Point($lon, $lat, $elevation); $point->setData($this->parseNodeProperties($node, $this->gpxTypes->get($node->nodeName . 'Type'))); if ($node->nodeName === 'rtept' && $this->parseGarminRpt) { foreach ($this->xpath->query('.//gpx:extensions/gpxx:RoutePointExtension/gpxx:rpt', $node) as $element) { $this->trackFromRoute[] = $this->parsePoint($element); } } return $point; } /** * @param DOMDocument $xmlObject * @return Point[] */ protected function parseWaypoints(DOMDocument $xmlObject): array { if (!in_array('wpt', $this->gpxTypes->get('gpxType'))) { return []; } $points = []; $wptElements = $xmlObject->getElementsByTagName('wpt'); foreach ($wptElements as $wpt) { $point = $this->parsePoint($wpt); $point->setData('gpxType', 'waypoint'); $points[] = $point; } return $points; } /** * @param DOMDocument $xmlObject * @return LineString[] */ protected function parseTracks(DOMDocument $xmlObject): array { if (!in_array('trk', $this->gpxTypes->get('gpxType'))) { return []; } $tracks = []; $trkElements = $xmlObject->getElementsByTagName('trk'); foreach ($trkElements as $trk) { $segments = []; /** @noinspection SpellCheckingInspection */ foreach ($this->childElements($trk, 'trkseg') as $trkseg) { $points = []; /** @noinspection SpellCheckingInspection */ foreach ($this->childElements($trkseg, 'trkpt') as $trkpt) { /** @var DomElement $trkpt */ $points[] = $this->parsePoint($trkpt); } // Avoids creating invalid LineString $segments[] = new LineString(count($points) <> 1 ? $points : []); } $track = count($segments) === 0 ? new LineString() : (count($segments) === 1 ? $segments[0] : new MultiLineString($segments)); $track->setData($this->parseNodeProperties($trk, $this->gpxTypes->get('trkType'))); $track->setData('gpxType', 'track'); $tracks[] = $track; } return $tracks; } /** * @param DOMDocument $xmlObject * @return LineString[] */ protected function parseRoutes(DOMDocument $xmlObject): array { if (!in_array('rte', $this->gpxTypes->get('gpxType'))) { return []; } $lines = []; $rteElements = $xmlObject->getElementsByTagName('rte'); foreach ($rteElements as $rte) { $components = []; /** @noinspection SpellCheckingInspection */ foreach ($this->childElements($rte, 'rtept') as $routePoint) { /** @var DomElement $routePoint */ $components[] = $this->parsePoint($routePoint); } $line = new LineString($components); $line->setData($this->parseNodeProperties($rte, $this->gpxTypes->get('rteType'))); $line->setData('gpxType', 'route'); $lines[] = $line; } return $lines; } /** * Parses a DOMNode and returns its content in a multidimensional associative array * eg: <wpt><name>Test</name><link href="example.com"><text>Example</text></link></wpt> * to: ['name' => 'Test', 'link' => ['text'] => 'Example', '@attributes' => ['href' => 'example.com']] * * @param DOMNode $node * @param string[]|null $tagList * @return string | array<string, string> | array<string, array<string>> */ protected static function parseNodeProperties(DOMNode $node, ?array $tagList = null) { if ($node->nodeType === XML_TEXT_NODE) { return $node->nodeValue; } $result = []; foreach ($node->childNodes as $childNode) { /** @var \DOMNode $childNode */ if ($childNode->hasChildNodes()) { if ($tagList === null || in_array($childNode->nodeName, $tagList ?: [])) { if ($node->firstChild->nodeName == $node->lastChild->nodeName && $node->childNodes->length > 1) { $result[$childNode->nodeName][] = self::parseNodeProperties($childNode); } else { $result[$childNode->nodeName] = self::parseNodeProperties($childNode); } } } elseif ($childNode->nodeType === 1 && in_array($childNode->nodeName, $tagList ?: [])) { $result[$childNode->nodeName] = self::parseNodeProperties($childNode); } elseif ($childNode->nodeType === 3) { $result = $childNode->nodeValue; } } if ($node->hasAttributes()) { if (is_string($result)) { // As of the GPX specification text node cannot have attributes, thus this never happens $result = ['#text' => $result]; } $attributes = []; foreach ($node->attributes as $attribute) { if ($attribute->name !== 'lat' && $attribute->name !== 'lon' && trim($attribute->value) !== '') { $attributes[$attribute->name] = trim($attribute->value); } } if (count($attributes)) { $result['@attributes'] = $attributes; } } return $result; } /** * Serialize geometries into a GPX string. * * @param Geometry $geometry * @param string|null $namespace * @param array<mixed>|null $allowedElements Which elements can be added to each GPX type * If not specified, every element defined in the GPX specification can be added * Can be overwritten with an associative array, with type name in keys. * eg.: ['wptType' => ['ele', 'name'], 'trkptType' => ['ele'], 'metadataType' => null] * @return string The GPX string representation of the input geometries */ public function write(Geometry $geometry, ?string $namespace = null, ?array $allowedElements = null): string { if ($namespace) { $this->nss = $namespace . ':'; } $this->gpxTypes = new GpxTypes($allowedElements); return '<?xml version="1.0" encoding="UTF-8"?> <' . $this->nss . 'gpx creator="geoPHP" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" > ' . $this->geometryToGPX($geometry) . '</' . $this->nss . 'gpx> '; } /** * @param Geometry|Collection $geometry * @return string */ protected function geometryToGPX($geometry) { switch ($geometry->geometryType()) { case Geometry::POINT: /** @var Point $geometry */ return $this->pointToGPX($geometry); case Geometry::LINE_STRING: case Geometry::MULTI_LINE_STRING: /** @var LineString $geometry */ return $this->linestringToGPX($geometry); case Geometry::POLYGON: case Geometry::MULTI_POINT: case Geometry::MULTI_POLYGON: case Geometry::GEOMETRY_COLLECTION: /** @var Collection $geometry */ return $this->collectionToGPX($geometry); } return ''; } /** * @param Point $geom * @param string $tag Can be "wpt", "trkpt" or "rtept" * @return string */ private function pointToGPX($geom, $tag = 'wpt') { if ($geom->isEmpty() || ($tag === 'wpt' && !in_array($tag, $this->gpxTypes->get('gpxType')))) { return ''; } $indent = $tag === 'trkpt' ? "\t\t" : ($tag === 'rtept' ? "\t" : ''); if ($geom->is3D() || $geom->getData() !== null) { $node = $indent . "<" . $this->nss . $tag . " lat=\"" . $geom->y() . "\" lon=\"" . $geom->x() . "\">\n"; if ($geom->is3D()) { $geom->setData('ele', $geom->z()); } $node .= self::processGeometryData($geom, $this->gpxTypes->get($tag . 'Type'), $indent . "\t") . $indent . "</" . $this->nss . $tag . ">\n"; if ($geom->is3D()) { $geom->setData('ele', null); } return $node; } return $indent . "<" . $this->nss . $tag . " lat=\"" . $geom->y() . "\" lon=\"" . $geom->x() . "\" />\n"; } /** * Writes a LineString or MultiLineString to the GPX * * The (Multi)LineString will be included in a <trk></trk> block * The LineString or each LineString of the MultiLineString will be in <trkseg> </trkseg> inside the <trk> * * @param LineString|MultiLineString $geom * @return string */ private function linestringToGPX($geom) { $isTrack = $geom->getData('gpxType') === 'route' ? false : true; if ($geom->isEmpty() || !in_array($isTrack ? 'trk' : 'rte', $this->gpxTypes->get('gpxType'))) { return ''; } if ($isTrack) { // write as <trk> /** @noinspection SpellCheckingInspection */ $gpx = "<" . $this->nss . "trk>\n" . self::processGeometryData($geom, $this->gpxTypes->get('trkType')); $components = $geom->geometryType() === 'LineString' ? [$geom] : $geom->getComponents(); foreach ($components as $lineString) { $gpx .= "\t<" . $this->nss . "trkseg>\n"; foreach ($lineString->getPoints() as $point) { $gpx .= $this->pointToGPX($point, 'trkpt'); } $gpx .= "\t</" . $this->nss . "trkseg>\n"; } /** @noinspection SpellCheckingInspection */ $gpx .= "</" . $this->nss . "trk>\n"; } else { // write as <rte> /** @noinspection SpellCheckingInspection */ $gpx = "<" . $this->nss . "rte>\n" . self::processGeometryData($geom, $this->gpxTypes->get('rteType')); foreach ($geom->getPoints() as $point) { $gpx .= $this->pointToGPX($point, 'rtept'); } /** @noinspection SpellCheckingInspection */ $gpx .= "</" . $this->nss . "rte>\n"; } return $gpx; } /** * @param Collection $geometry * @return string */ public function collectionToGPX(Collection $geometry): string { $metadata = self::processGeometryData($geometry, $this->gpxTypes->get('metadataType')); $metadata = empty($metadata) || !in_array('metadataType', $this->gpxTypes->get('gpxType')) ? '' : "<metadata>\n{$metadata}</metadata>\n\n"; $wayPoints = $routes = $tracks = ""; foreach ($geometry->getComponents() as $component) { $type = $component->geometryType(); if (strpos($type, 'Point') !== false) { $wayPoints .= $this->geometryToGPX($component); } if (strpos($type, 'LineString') !== false && $component->getData('gpxType') === 'route') { $routes .= $this->geometryToGPX($component); } if (strpos($type, 'LineString') !== false && $component->getData('gpxType') !== 'route') { $tracks .= $this->geometryToGPX($component); } if (strpos($type, 'Point') === false && strpos($type, 'LineString') === false) { return $this->geometryToGPX($component); } } return $metadata . $wayPoints . $routes . $tracks; } /** * @param Geometry $geometry * @param string[] $tagList Allowed tags * @param string $indent * @return string */ protected static function processGeometryData(Geometry $geometry, array $tagList, string $indent = "\t"): string { $tags = ''; if ($geometry->getData() !== null) { foreach ($tagList as $tagName) { if ($geometry->hasDataProperty($tagName)) { $tags .= self::createNodes($tagName, $geometry->getData($tagName), $indent) . "\n"; } } } return $tags; } /** * @param string $tagName * @param string|array<string, mixed> $value * @param string $indent * @return string */ protected static function createNodes(string $tagName, $value, string $indent): string { $attributes = ''; if (!is_array($value)) { $returnValue = $value; } else { $returnValue = ''; if (array_key_exists('@attributes', $value)) { $attributes = ''; foreach ($value['@attributes'] as $attributeName => $attributeValue) { $attributes .= ' ' . $attributeName . '="' . $attributeValue . '"'; } unset($value['@attributes']); } foreach ($value as $subKey => $subValue) { $returnValue .= "\n" . self::createNodes($subKey, $subValue, $indent . "\t") . "\n" . $indent; } } return $indent . "<{$tagName}{$attributes}>{$returnValue}</{$tagName}>"; } } <?php /* * This file is part of the GeoPHP package. * Copyright (c) 2011 - 2016 Patrick Hayes and contributors * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace geoPHP; use geoPHP\Adapter\GeoHash; use geoPHP\Exception\IOException; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\GeometryCollection; use function is_array; use function array_key_exists; use function array_merge; use function array_search; use function array_shift; use function array_unique; use function call_user_func_array; use function class_exists; use function count; use function current; use function explode; use function fopen; use function fread; use function fseek; use function fwrite; use function hex2bin; use function ltrim; use function preg_match; use function strlen; use function strpos; use function strstr; use function substr; use function trim; use function unpack; /** * Porides constants and static methods for loading geometries in any supported format. */ // @codingStandardsIgnoreLine class geoPHP { // Earth radius constants in meters /** WGS84 semi-major axis (a), aka equatorial radius */ const EARTH_WGS84_SEMI_MAJOR_AXIS = 6378137.0; /** WGS84 semi-minor axis (b), aka polar radius */ const EARTH_WGS84_SEMI_MINOR_AXIS = 6356752.314245; /** WGS84 inverse flattening */ const EARTH_WGS84_FLATTENING = 298.257223563; /** WGS84 semi-major axis (a), aka equatorial radius */ const EARTH_GRS80_SEMI_MAJOR_AXIS = 6378137.0; /** GRS80 semi-minor axis */ const EARTH_GRS80_SEMI_MINOR_AXIS = 6356752.314140; /** GRS80 inverse flattening */ const EARTH_GRS80_FLATTENING = 298.257222100882711; /** IUGG mean radius R1 = (2a + b) / 3 */ const EARTH_MEAN_RADIUS = 6371008.8; /** IUGG R2: Earth's authalic ("equal area") radius is the radius of a hypothetical perfect sphere * which has the same surface area as the reference ellipsoid. */ const EARTH_AUTHALIC_RADIUS = 6371007.2; /** * @var array<string, string> */ private static $adapterMap = [ 'wkt' => 'WKT', 'ewkt' => 'EWKT', 'wkb' => 'WKB', 'ewkb' => 'EWKB', 'json' => 'GeoJSON', 'geojson' => 'GeoJSON', 'kml' => 'KML', 'gpx' => 'GPX', 'georss' => 'GeoRSS', 'google_geocode' => 'GoogleGeocode', 'geohash' => 'GeoHash', 'twkb' => 'TWKB', 'osm' => 'OSM', ]; /** * @var array<string, string> */ private static $geometryList = [ 'point' => 'Point', 'linestring' => 'LineString', 'polygon' => 'Polygon', 'multipoint' => 'MultiPoint', 'multilinestring' => 'MultiLineString', 'multipolygon' => 'MultiPolygon', 'geometrycollection' => 'GeometryCollection', ]; /** * @var bool|null */ private static $geosInstalled; /** * Get a list of adapters as an array keyed by the value that should be passed to geoPHP::load. * * @return array<string, string> Returns the map of supported adapters and formats */ public static function getAdapterMap(): array { return self::$adapterMap; } /** * List all geometry types. * * @return array<string, string> */ public static function getGeometryList(): array { return self::$geometryList; } /** * Load from an adapter format (like wkt) into a Geometry. * * If $data is array, all passed values will be combined into a single geometry. * * @param string|string[]|Geometry|Geometry[] $data The data in any supported format, including geoPHP Geometry. * @param string $format Format of the data ('wkt','wkb','json', …). Tries to detect automatically if omitted. * @param mixed $args Further arguments will be passed to the geo adapter. * * @throws \Exception|IOException * * @return Geometry */ public static function load($data, string $format = null, ...$args): Geometry { if (is_array($data)) { // Data is an array, combine all passed in items into a single geometry. $geometries = []; foreach ($data as $item) { $geometries[] = static::load($item, $format, $args); } return geoPHP::buildGeometry($geometries); } // Auto-detect type if needed if (!$format) { // If the input is already a Geometry, just pass it back. if ($data instanceof Geometry) { return $data; } $detectedFormat = geoPHP::detectFormat($data); if (!$detectedFormat) { throw new IOException("Can not detect format"); } $formatParts = explode(':', $detectedFormat); $format = array_shift($formatParts); $args = $formatParts ?: $args; } if (!array_key_exists($format, self::$adapterMap)) { throw new IOException('geoPHP could not find an adapter of type ' . htmlentities($format)); } $adapterType = 'geoPHP\\Adapter\\' . self::$adapterMap[$format]; $adapter = new $adapterType(); return call_user_func_array([$adapter, "read"], array_merge([$data], $args)); } /** * Sets and/or returns static geosInstalled property. * * @param bool $force * * @return bool * * @deprecated 2.1 Use instead isGeosInstalled(), enableGeos() or disableGeos(). */ public static function geosInstalled(bool $force = null): bool { geoPHP::$geosInstalled = null; if ($force !== null) { geoPHP::$geosInstalled = $force; } if (getenv('GEOS_DISABLED') == 1) { geoPHP::$geosInstalled = false; } if (geoPHP::$geosInstalled !== null) { return geoPHP::$geosInstalled; } geoPHP::$geosInstalled = class_exists('GEOSGeometry', false); return geoPHP::$geosInstalled; } /** * Returns if Geos support is installed and enabled. * * Checks availability of Geos library. * Geos support can be forced to disable by setting the environment variable "GEOS_DISABLED = 1". * * @return boolean */ public static function isGeosInstalled(): bool { if (getenv('GEOS_DISABLED') === '1') { return geoPHP::$geosInstalled = false; } elseif (geoPHP::$geosInstalled === null) { geoPHP::$geosInstalled = class_exists('GEOSGeometry', false); } return geoPHP::$geosInstalled; } /** * Attempts to enable Geos support, and returns its status. * * @return boolean Returns status of Geos support. */ public static function enableGeos(): bool { geoPHP::$geosInstalled = null; return geoPHP::isGeosInstalled(); } /** * Disables Geos support. * * Useful for development. * * @return void */ public static function disableGeos(): void { geoPHP::$geosInstalled = false; } /** * Converts a GEOS Geometry to native geoPHP geometry. * * Writes GEOSGeometry to WKB using GEOSWKBWriter, * and reads that using the native WKB adapter. * Saves the original GEOSGeometry in "geos" property of the crated Geometry. * Supports SRID and Z-dimension. * * @param \GEOSGeometry $geos * * @throws \Exception * @throws IOException * * @return Geometry|null Returns the converted geoPHP Geometry or null if GEOS is not installed. * * @codeCoverageIgnore */ public static function geosToGeometry(\GEOSGeometry $geos): ?Geometry { if (!geoPHP::isGeosInstalled()) { return null; } $wkbWriter = new \GEOSWKBWriter(); $wkbWriter->setOutputDimension($geos->coordinateDimension()); $wkbWriter->setIncludeSRID(true); // @phpstan-ignore-line $wkb = $wkbWriter->writeHEX($geos); return geoPHP::load($wkb, 'ewkb', true); } /** * Reduces a geometry, or an array of geometries, into their 'lowest' available common geometry. * * For example a GeometryCollection of only points will become a MultiPoint. * A multi-point containing a single point will return a point. * An array of geometries can be passed and they will be compiled into a single geometry * * @param Geometry|Geometry[] $geometries * @return Geometry|null */ public static function geometryReduce($geometries) { if ($geometries === null) { return null; } /* * If it is a single geometry */ if ($geometries instanceof Geometry) { // If the geometry cannot even theoretically be reduced more, then pass it back $singleGeometries = ['Point', 'LineString', 'Polygon']; if (in_array($geometries->geometryType(), $singleGeometries)) { return $geometries; } // If it is a multi-geometry, check to see if it just has one member // If it does, then pass the member, if not, then just pass back the geometry if (strpos($geometries->geometryType(), 'Multi') === 0) { $components = $geometries->getComponents(); if (count($components) == 1) { return $components[0]; } else { return $geometries; } } } elseif (is_array($geometries) && count($geometries) == 1) { // If it's an array of one, then just parse the one return geoPHP::geometryReduce(array_shift($geometries)); } if (!is_array($geometries)) { $geometries = [$geometries]; } /** * So now we either have an array of geometries * @var Geometry[] $geometries */ $reducedGeometries = []; $geometryTypes = []; self::explodeCollections($geometries, $reducedGeometries, $geometryTypes); $geometryTypes = array_unique($geometryTypes); if (empty($geometryTypes)) { return null; // TODO: Never happens? } if (count($geometryTypes) == 1) { if (count($reducedGeometries) == 1) { return $reducedGeometries[0]; } else { $class = 'geoPHP\\Geometry\\' . (strstr($geometryTypes[0], 'Multi') ? '' : 'Multi') . $geometryTypes[0]; /** @var Geometry */ $geometry = new $class($reducedGeometries); return $geometry; } } else { return new GeometryCollection($reducedGeometries); } } /** * @param Geometry[] $unreduced * @param Geometry[] $reduced * @param array<string> $types * * @return void */ private static function explodeCollections(array $unreduced, array &$reduced, array &$types): void { foreach ($unreduced as $item) { if ($item->geometryType() == 'GeometryCollection' || strpos($item->geometryType(), 'Multi') === 0) { self::explodeCollections($item->getComponents(), $reduced, $types); } else { $reduced[] = $item; $types[] = $item->geometryType(); } } } /** * Build an appropriate Geometry, MultiGeometry, or GeometryCollection to contain the Geometries in it. * * @see geos::geom::GeometryFactory::buildGeometry * * @param Geometry|Geometry[] $geometries * * @throws \Exception * * @return Geometry A Geometry of the "smallest", "most type-specific" class that can contain the elements. */ public static function buildGeometry($geometries): Geometry { if (empty($geometries)) { return new GeometryCollection(); } /* If it is a single geometry */ if ($geometries instanceof Geometry) { return $geometries; } elseif (!is_array($geometries)) { throw new \InvalidArgumentException('Input of buildGeometry() must be Geometry or array of Geometries'); } elseif (count($geometries) == 1) { // If it's an array of one, then just parse the one return geoPHP::buildGeometry(array_shift($geometries)); } /** * So now we either have an array of geometries * @var Geometry[] $geometries */ $geometryTypes = []; $hasData = false; foreach ($geometries as $item) { $geometryTypes[] = $item->geometryType(); if ($item->getData() !== null) { $hasData = true; } } $geometryTypes = array_unique($geometryTypes); if (count($geometryTypes) == 1 && !$hasData) { if ($geometryTypes[0] === Geometry::GEOMETRY_COLLECTION) { return new GeometryCollection($geometries); } if (count($geometries) == 1) { return $geometries[0]; } else { $newType = (strpos($geometryTypes[0], 'Multi') !== false ? '' : 'Multi') . $geometryTypes[0]; foreach ($geometries as $geometry) { if ($geometry->isEmpty()) { return new GeometryCollection($geometries); } } $class = 'geoPHP\\Geometry\\' . $newType; /** @var Geometry */ $geometry = new $class($geometries); return $geometry; } } else { return new GeometryCollection($geometries); } } /** * Detects format of the given input. * * This function is meant to be SPEEDY. * It could make a mistake in XML detection if you are mixing or using namespaces in weird ways * (ie, KML inside an RSS feed). * * @param string $input * * @return string|null Returns the name of input's format (e.g. 'gpx') or null if fails to detect. */ public static function detectFormat(string &$input): ?string { $mem = fopen('php://memory', 'x+'); if (!$mem) { throw new IOException('Failed to allocate memory'); } fwrite($mem, $input, 11); // Write 11 bytes - we can detect the vast majority of formats in the first 11 bytes fseek($mem, 0); $bin = fread($mem, 11) ?: ''; $bytes = unpack("c*", $bin) ?: []; // If bytes is empty, then we were passed empty input if (empty($bytes)) { return null; } // First char is a tab, space or carriage-return. trim it and try again if ($bytes[1] == 9 || $bytes[1] == 10 || $bytes[1] == 32) { $input = ltrim($input); return geoPHP::detectFormat($input); } // Detect WKB or EWKB -- first byte is 1 (little endian indicator) if ($bytes[1] == 1 || $bytes[1] == 0) { $wkbType = current((array) unpack($bytes[1] == 1 ? 'V' : 'N', substr($bin, 1, 4))); if (array_search($wkbType & 0xF, Adapter\WKB::$typeMap)) { // If SRID byte is TRUE (1), it's EWKB if (($wkbType & Adapter\WKB::SRID_MASK) === Adapter\WKB::SRID_MASK) { return 'ewkb'; } else { return 'wkb'; } } } /* Detect HEX encoded WKB or EWKB (PostGIS format) * first byte is 48, second byte is 49 (hex '01' => first-byte = 1) * The shortest possible WKB string (LINESTRING EMPTY) is 18 hex-chars (9 encoded bytes) long * This differentiates it from a geohash, which is always shorter than 13 characters. */ if ($bytes[1] == 48 && ($bytes[2] == 49 || $bytes[2] == 48) && strlen($input) > 12) { if ( (current((array) unpack($bytes[2] == 49 ? 'V' : 'N', (string) hex2bin((string) substr($bin, 2, 8)))) & Adapter\WKB::SRID_MASK) == Adapter\WKB::SRID_MASK ) { return 'ewkb:true'; } else { return 'wkb:true'; } } // Detect GeoJSON - first char starts with { if ($bytes[1] == 123) { return 'json'; } // Detect EWKT - strats with "SRID=number;" if (substr($input, 0, 5) === 'SRID=') { return 'ewkt'; } // Detect WKT - starts with a geometry type name if (Adapter\WKT::getWktType(strstr($input, ' ', true) ?: '')) { return 'wkt'; } // Detect XML -- first char is < if ($bytes[1] == 60) { // grab the first 1024 characters $string = substr($input, 0, 1024); if (strpos($string, '<kml') !== false) { return 'kml'; } if (strpos($string, '<coordinate') !== false) { return 'kml'; } if (strpos($string, '<gpx') !== false) { return 'gpx'; } if (strpos($string, '<osm ') !== false) { return 'osm'; } if (preg_match('/<[a-z]{3,20}>/', $string) !== false) { return 'georss'; } } // We need an 8 byte string for geohash and unpacked WKB / WKT fseek($mem, 0); $string = trim(fread($mem, 8) ?: ''); // Detect geohash - geohash ONLY contains lowercase chars and numerics preg_match('/[' . GeoHash::$characterTable . ']+/', $string, $matches); if (isset($matches[0]) && $matches[0] == $string && strlen($input) <= 13) { return 'geohash'; } preg_match('/^[a-f0-9]+$/', $string, $matches); if (isset($matches[0])) { return 'twkb:true'; } else { return 'twkb'; } } } <?php namespace geoPHP\Exception; use Throwable; /** */ interface Exception extends Throwable { } <?php namespace geoPHP\Exception; use LogicException; /** * Exception thrown if a method is not implemented yet */ class UnsupportedMethodException extends LogicException implements Exception { /** * @param string $method Name of the unsupported method * @param string|null $message Additional message * @param int $code */ public function __construct(string $method, ?string $message = null, int $code = 0, Exception $previous = null) { $message = 'Method ' . $method . '() is not supported yet.' . ($message ? ' ' . $message : ''); parent::__construct($message, $code, $previous); } /** * Method is supported only with GEOS installed * * @param string $methodName Name of the unsupported method * @return UnsupportedMethodException */ public static function geos(string $methodName): self { return new self($methodName, 'Please install GEOS extension.', 1); } } <?php namespace geoPHP\Exception; use Throwable; /** * Exception thrown if an adapter can't parse input data. */ class FileFormatException extends IOException { /** * @param string|null $message Additional message * @param string|null $invalidData The data that couldn't be interpreted. Will be cut down to a short sample. * @param int $code * @param Throwable|null $previous */ public function __construct( ?string $message = null, string $invalidData = null, int $code = 0, ?Throwable $previous = null ) { if ($invalidData) { $invalidData = strlen($invalidData) <= 50 ? $invalidData : substr($invalidData, 0, 49) . "…"; $message .= " Data: \"$invalidData\""; } parent::__construct($message, $code, $previous); } } <?php namespace geoPHP\Exception; use RuntimeException; /** * Exception thrown if a geometry doesn't meet the basic requirements of validity * Eg. a LineString with only one point */ class InvalidGeometryException extends RuntimeException implements Exception { } <?php namespace geoPHP\Exception; use Throwable; /** * Exception thrown if XML parser can't load input data. */ class InvalidXmlException extends IOException { /** * @param string|null $message Additional message * @param int $code * @param Throwable|null $previous */ public function __construct(?string $message = null, int $code = 0, ?Throwable $previous = null) { $message = 'Invalid XML.' . ($message ? ' ' . $message : ''); parent::__construct($message, $code, $previous); } } <?php namespace geoPHP\Exception; use RuntimeException; use Throwable; /** * Exception thrown when an error occurs on reading or writing */ class IOException extends RuntimeException implements Exception { /** * @param string|null $message Additional message * @param int $code * @param Throwable|null $previous */ public function __construct(?string $message = null, int $code = 0, ?Throwable $previous = null) { $message = 'IO error: ' . $message; parent::__construct($message, $code, $previous); } } includes: - phpstan-baseline.neon parameters: level: 6 paths: - src/ - tests/ inferPrivatePropertyTypeFromConstructor: true treatPhpDocTypesAsCertain: false reportUnmatchedIgnoredErrors: false ignoreErrors: - '#\:\:centroid\(\) should return geoPHP\\Geometry\\Point but returns geoPHP\\Geometry\\Geometry\|null.$#' - '#\:\:boundary\(\) should return geoPHP\\Geometry\\.+ but returns geoPHP\\Geometry\\Geometry\|null.$#' - '#Parameter \#1 \$connection of function pg_query expects resource, PgSql\\Connection given.$#' - '#Parameter \#1 \$result of function pg_fetch_all expects PgSql\\Result, resource\|false given.$#'parameters: ignoreErrors: <?xml version="1.0" encoding="UTF-8"?> <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" colors="true" beStrictAboutTestsThatDoNotTestAnything="true" beStrictAboutOutputDuringTests="true" verbose="false"> <testsuites> <testsuite name="funiQ/geoPHP tests"> <directory>tests/unit/Geometry</directory> <directory>tests/unit/Exception</directory> <directory>tests/unit/Adapter</directory> </testsuite> </testsuites> <coverage includeUncoveredFiles="true" cacheDirectory="build/cache/phpunit"> <include> <directory>src/</directory> </include> <report> <text outputFile="php://stdout" showUncoveredFiles="false" showOnlySummary="false"/> <clover outputFile="build/logs/clover.xml"/> <html outputDirectory="build/coverage"/> </report> </coverage> <php> <env name="GEOS_DISABLED" value="1"/> </php> </phpunit> <div id="wiki-wrapper" class="page"> <div id="head"> <h1 class="instapaper_title">API Reference</h1> <ul class="wiki-actions readability-extra"> <li class="gollum-minibutton"> <a href="/funiq/geoPHP/wiki/API-Reference/_history" class="minibutton bigger action-page-history"> <span>Page History</span> </a> </li> </ul> </div> <div id="wiki-content"> <div class="wrap"> <div id="wiki-body" class="gollum-mediawiki-content instapaper_body"> <div class="markdown-body"> <table class="toc" summary="Contents"> <tr> <td> <div>Table of Contents</div> <ul> <ul> <li> <a href="#wiki-geoPHP_static_class" rel="nofollow">geoPHP static class</a> <ul> <li> <a href="#wiki-Example" rel="nofollow">Example</a> </li> <li> <a href="#wiki-Static_Methods" rel="nofollow">Static Methods</a> </li> </ul> </li> <li> <a href="#wiki-Adapters" rel="nofollow">Adapters</a> <ul> <li> <a href="#wiki-Class_Hierarchy" rel="nofollow">Class Hierarchy</a> </li> <li> <a href="#wiki-Example-2" rel="nofollow">Example</a> </li> <li> <a href="#wiki-Methods" rel="nofollow">Methods</a> </li> </ul> </li> <li> <a href="#wiki-Geometries" rel="nofollow">Geometries</a> <ul> <li> <a href="#wiki-Class_Hierarchy-2" rel="nofollow">Class Hierarchy</a> </li> <li> <a href="#wiki-Example-3" rel="nofollow">Example</a> </li> <li> <a href="#wiki-Methods-2" rel="nofollow">Methods</a> </li> </ul> </li> </ul> </ul> </td> </tr> </table> <h2> <span class="mw-headline"> <a name="wiki-geoPHP_static_class" rel="nofollow">geoPHP static class</a> </span> </h2> <p>geoPHP provides a static class that contains useful utility functions. All methods must be called statically. </p> <h3> <span class="mw-headline"> <a name="wiki-Example" rel="nofollow">Example</a> </span> </h3> <pre>$geometry = geoPHP::load('MULTILINESTRING((10 10,20 20,10 40))','wkt');</pre> <pre>$reduced_geometry = geoPHP::geometryReduce($geometry);</pre> <h3> <span class="mw-headline"> <a name="wiki-Static_Methods" rel="nofollow">Static Methods</a> </span> </h3> <table> <tr> <td> <b>Method</b> </td> <td> <b>Description</b> </td> <td> <b>Returns</b> </td> </tr> <tr> <td> version </td> <td> Provides the current geoPHP version. Useful if you need to check compatablity </td> <td> Numeric String </td> </tr> <tr> <td> load </td> <td> Load from an adapter format (like wkt) into a geometry. The first argument is the data, the second one is the format of the data ('wkt','wkb','json','kml','gpx','google_geocode'). All additional arguments are passed along to the read method of the relevant adapte </td> <td> Geometry </td> </tr> <tr> <td> getAdapterMap </td> <td> Get a list of adapters as an array keyed by the value that should be passed to geoPHP::load </td> <td> Array </td> </tr> <tr> <td> geometryList </td> <td> List all geometry types </td> <td> Array </td> </tr> <tr> <td> geosToGeometry </td> <td> Given a GEOSGeometry, get a geoPHP geometry </td> <td> Geometry </td> </tr> <tr> <td> geometryReduce </td> <td> Reduce a geometry, or an array of geometries, into their \'lowest\' available common geometry. For example a GeometryCollection of only points will become a MultiPoint, while a multi-point containing a single point will return a point. An array of geometries can be passed and they will be compiled into a single geometry. </td> <td> Geometry </td> </tr> <tr> <td> geosInstalled </td> <td> Check if the GEOS php extension is installed and working </td> <td> Boolean </td> </tr> </table> <h2> <span class="mw-headline"> <a name="wiki-Adapters" rel="nofollow">Adapters</a> </span> </h2> <p>Adapters are responsible for getting data in and out of geoPHP Geometries. Generally you will use an adapter to load data into a geoPHP geometry, do various manipulations on the geometry, then use another adapter to write it out to another (or the same) format. You can also use adapters by themselves to simply do conversion from one format to another (See <a class="internal present" href="/funiq/geoPHP/wiki/Example-format-converter" rel="nofollow" >example-format-converter</a> for an example of this). Adapters should be instantiated and not called statically. </p> <h3> <span class="mw-headline"> <a name="wiki-Class_Hierarchy" rel="nofollow">Class Hierarchy</a> </span> </h3> <ul> <li>GeoAdapter Abtract Class<ul> <li>WKT Enables reading and writing WKT (Well Known Text)</li> <li>WKB Enables reading and writing WKB (Well Known Binary). This is very fast.</li> <li>GeoJSON Enables reading and writing GeoJSON</li> <li>KML Enables reading and writing KML (Google Earth)</li> <li>GoogleGeocode Enables geocoding and reverse-geocoding via google geocoding API</li> <li>GPX Enables reading and writing GPX (from handheld GPS devices)</li> <li>GeoRSS Enables reading and writing of GeoRSS</li> </ul> </li> </ul> <h3> <span class="mw-headline"> <a name="wiki-Example-2" rel="nofollow">Example</a> </span> </h3> <pre>$wkb_reader = new WKB();</pre> <pre>$geometry = $wkb_reader->read('0101000000000000000000f03f000000000000f03f',TRUE);</pre> <pre>$wkt_writer = new wkt();</pre> <pre>$wkt = $wkt_writer->write($geometry);</pre> <h3> <span class="mw-headline"> <a name="wiki-Methods" rel="nofollow">Methods</a> </span> </h3> <table> <tr> <td> <b>Method</b> </td> <td> <b>Description</b> </td> <td> <b>Returns</b> </td> </tr> <tr> <td> read </td> <td> Read in input (generally a string) and return a Geometry </td> <td> Geometry </td> </tr> <tr> <td> write </td> <td> Write out the given geometry into the adapter formater </td> <td> String </td> </tr> </table> <h2> <span class="mw-headline"> <a name="wiki-Geometries" rel="nofollow">Geometries</a> </span> </h2> <p>Geometries form the heart of the geoPHP library. Once you have loaded your data into a Geometry object, you have access to all the various methods detailed below for doing conversions, transformations, and operations. While generally you would use an adapter to get a Geometry object, they can also be built by hand. See the constructor methods in the classes to see how to do this. GEOS-php extension needs to be installed in order to use some of the advanced methods (detailed below). </p> <h3> <span class="mw-headline"> <a name="wiki-Class_Hierarchy-2" rel="nofollow">Class Hierarchy</a> </span> </h3> <ul> <li>Geometry<ul> <li>Point</li> <li>Collection<ul> <li>LineString</li> <li>Polygon</li> <li>MultiLineString</li> <li>MultiPoint</li> <li>MultiPolygon</li> <li>GeometryCollection</li> </ul> </li> </ul> </li> </ul> <h3> <span class="mw-headline"> <a name="wiki-Example-3" rel="nofollow">Example</a> </span> </h3> <pre>$poly1 = $geoPHP::load('POLYGON((30 10,10 20,20 40,40 40,30 10))','wkt');</pre> <pre>$poly2 = $geoPHP::load('POLYGON((35 10,10 20,15 40,45 45,35 10),(20 30, 35 35, 30 20, 20 30))','wkt');</pre> <pre>$combined_poly = $poly1->union($poly2);</pre> <pre>$kml = $combined_poly->out('kml');</pre> <h3> <span class="mw-headline"> <a name="wiki-Methods-2" rel="nofollow">Methods</a> </span> </h3> <p> <b>Common Methods</b> </p> <table> <tr> <td> <b>Method</b> </td> <td> <b>Description</b> </td> <td> <b>Returns</b> </td> </tr> <tr> <td> out </td> <td> Outputs the geometry into the specified adapter format. Available formats are <i>wkt</i>, <i>wkb</i>, <i>json</i>, <i>kml</i>, <i>gpx</i>, <i>google_geocode</i> </td> <td> String </td> </tr> <tr> <td> area </td> <td> The area of this Polygon (or GeometryCollection), as measured in the spatial reference system of the geometry </td> <td> Float </td> </tr> <tr> <td> boundary </td> <td> Returns the closure of the combinatorial boundary of this geometric object. </td> <td> LinearRing </td> </tr> <tr> <td> envelope </td> <td> The minimum bounding box for this Geometry, returned as a Geometry. </td> <td> Polygon </td> </tr> <tr> <td> getBBox </td> <td> The minimum bounding box for this Geometry, returned as an array. Also see envelope() </td> <td> Array </td> </tr> <tr> <td> centroid </td> <td> The mathematical centroid for this geometry as a Point. For polygons, the result is not guaranteed to be interior. </td> <td> Point </td> </tr> <tr> <td> length </td> <td> The length of this Curve in its associated spatial reference. </td> <td> Float </td> </tr> <tr> <td> y </td> <td> The y-coordinate value for this Point. </td> <td> Float </td> </tr> <tr> <td> x </td> <td> The x-coordinate value for this Point. </td> <td> Float </td> </tr> <tr> <td> numGeometries </td> <td> The number of component geometries in this collection </td> <td> Integer </td> </tr> <tr> <td> geometryN </td> <td> Returns the geometry N in this collection. Note that the index starts at 1. </td> <td> Geometry </td> </tr> <tr> <td> startPoint </td> <td> The start Point of this LineString </td> <td> Point </td> </tr> <tr> <td> endPoint </td> <td> The end Point of this LineString </td> <td> Point </td> </tr> <tr> <td> isRing </td> <td> Returns 1 (TRUE) if this Curve is closed() and this Curve isSimple(). </td> <td> Boolean </td> </tr> <tr> <td> isClosed </td> <td> Returns 1 (TRUE) if this Curve is closed. StartPoint() == EndPoint(). </td> <td> Boolean </td> </tr> <tr> <td> getComponents </td> <td> Get all sub-geometry components of the geometry </td> <td> Array of geometries </td> </tr> <tr> <td> numPoints </td> <td> The number of Points in this LineString </td> <td> Integer </td> </tr> <tr> <td> pointN </td> <td> Returns the specified Point N in this LineString. Note that the index starts at 1. </td> <td> Point </td> </tr> <tr> <td> exteriorRing </td> <td> Returns the exterior ring of this Polygon. </td> <td> LineString </td> </tr> <tr> <td> numInteriorRings </td> <td> Returns the number of interior rings in this Polygon. </td> <td> Integer </td> </tr> <tr> <td> interiorRingN </td> <td> Returns the Nth interior ring for this Polygon as a LineString. Note that the index starts at 1. </td> <td> LineString </td> </tr> <tr> <td> dimension </td> <td> The inherent dimension of this geometric object. In non-homogeneous collections, this will return the largest topological dimension of the contained objects. </td> <td> Integer </td> </tr> <tr> <td> geometryType </td> <td> Returns the name of the instantiable subtype of Geometry of which this geometric object is an instantiable member. The name of the subtype of Geometry is returned as a string. </td> <td> String </td> </tr> <tr> <td> getSRID </td> <td> Returns the Spatial Reference System ID for this geometric object. </td> <td> integer </td> </tr> <tr> <td> setSRID </td> <td> Set the Spatial Reference System ID for this geometric object. </td> <td> NULL </td> </tr> <tr> <td> asArray </td> <td> Get the given geometry as an array of components (recursive) </td> <td> Array </td> </tr> <tr> <td> getGeoInterface </td> <td> Get the geometryType and Coordinates as an array </td> <td> Array </td> </tr> </table> <p> <b>Aliases</b> </p> <table> <tr> <td> <b>Method</b> </td> <td> <b>Description</b> </td> <td> <b>Returns</b> </td> </tr> <tr> <td> getCentroid </td> <td> Alias for centroid() </td> <td> Point </td> </tr> <tr> <td> getArea </td> <td> Alias for area() </td> <td> Float </td> </tr> <tr> <td> getX </td> <td> Alias for x() </td> <td> Float </td> </tr> <tr> <td> getY </td> <td> Alias for y() </td> <td> Float </td> </tr> <tr> <td> getGeos </td> <td> Alias for geos() </td> <td> GEOSGeometry </td> </tr> <tr> <td> getGeomType </td> <td> Alias for geometryType() </td> <td> String </td> </tr> <tr> <td> SRID </td> <td> Alias for getSRID() </td> <td> Integer </td> </tr> <tr> <td> asText </td> <td> Alias for <i>$this->out('wkt')</i> </td> <td> String </td> </tr> <tr> <td> asBinary </td> <td> Alias for <i>$this->out('wkb')</i> </td> <td> String </td> </tr> </table> <p> <b>Advanced Methods</b> </p> <p>The <a class="internal present" href="/funiq/geoPHP/wiki/GEOS" rel="nofollow">GEOS-php extension</a> needs to be installed for these functions to be available </p> <table> <tr> <td> <b>Method</b> </td> <td> <b>Description</b> </td> <td> <b>Returns</b> </td> </tr> <tr> <td> geos </td> <td> Return a GEOSGeometry object representing this geometry </td> <td> GEOSGeometry </td> </tr> <tr> <td> setGeos </td> <td> Set a GEOSGeometry object representing this geometry </td> <td> NULL </td> </tr> <tr> <td> pointOnSurface </td> <td> A Point guaranteed to be within a polygon </td> <td> Point </td> </tr> <tr> <td> equals </td> <td> Returns 1 (TRUE) if this geometry is “spatially equal” to another Geometry </td> <td> Boolean </td> </tr> <tr> <td> equalsExact </td> <td> Returns 1 (TRUE) if this gemometric object is exactly the same as another object, including the ordering of component parts </td> <td> Boolean </td> </tr> <tr> <td> relate </td> <td> Returns 1 (TRUE) if this geometric object is spatially related to anotherGeometry by testing for intersections between the interior, boundary and exterior of the two geometric objects as specified by the values in the intersectionPatternMatrix. This returns FALSE if all the tested intersections are empty except exterior (this) intersect exterior (another). </td> <td> Boolean </td> </tr> <tr> <td> checkValidity </td> <td> Boolean </td> </tr> <tr> <td> isSimple </td> <td> Returns 1 (TRUE) if this geometry does not pass through the same point in space more than once </td> <td> Boolean </td> </tr> <tr> <td> project </td> <td> Project the geometry from from one SRID to another </td> <td> Geometry </td> </tr> <tr> <td> buffer </td> <td> Returns a geometric object that represents all Points whose distance from this geometric object is less than or equal to distance. Calculations are in the spatial reference system of this geometric object. Because of the limitations of linear interpolation, there will often be some relatively small error in this distance, but it should be near the resolution of the coordinates used. </td> <td> Geometry </td> </tr> <tr> <td> intersection </td> <td> Returns a geometric object that represents the Point set intersection of this geometric object with anotherGeometry. See <a href="http://en.wikipedia.org/wiki/Intersection_(set_theory)" rel="nofollow" >http://en.wikipedia.org/wiki/Intersection_(set_theory)</a> </td> <td> Geometry </td> </tr> <tr> <td> convexHull </td> <td> Returns a geometric object that represents the convex hull of this geometric object. See <a href="http://en.wikipedia.org/wiki/Convex_hull" rel="nofollow" >http://en.wikipedia.org/wiki/Convex_hull</a> </td> <td> Geometry </td> </tr> <tr> <td> difference </td> <td> Returns a geometric object that represents the Point set difference of this geometric object with anotherGeometry. </td> <td> Geometry </td> </tr> <tr> <td> symDifference </td> <td> Returns a geometric object that represents the Point set symmetric difference of this geometric object with another Geometry. See <a href="http://en.wikipedia.org/wiki/Symmetric_difference" rel="nofollow" >http://en.wikipedia.org/wiki/Symmetric_difference</a> </td> <td> Geometry </td> </tr> <tr> <td> union </td> <td> Returns a geometric object that represents the Point set union of this geometric object with anotherGeometry. See <a href="http://en.wikipedia.org/wiki/Union_(set_theory)" rel="nofollow" >http://en.wikipedia.org/wiki/Union_(set_theory)</a> </td> <td> Geometry </td> </tr> <tr> <td> simplify </td> <td> Simplify the geometry </td> <td> Geometry </td> </tr> <tr> <td> disjoint </td> <td> Returns 1 (TRUE) if this geometric object is “spatially disjoint” from another Geometry. </td> <td> Boolean </td> </tr> <tr> <td> touches </td> <td> Returns 1 (TRUE) if this geometric object “spatially touches” another Geometry. </td> <td> Boolean </td> </tr> <tr> <td> intersects </td> <td> Returns 1 (TRUE) if this geometric object “spatially intersects” another Geometry. </td> <td> Boolean </td> </tr> <tr> <td> crosses </td> <td> Returns 1 (TRUE) if this geometric object “spatially crosses? another Geometry. </td> <td> Boolean </td> </tr> <tr> <td> within </td> <td> Returns 1 (TRUE) if this geometric object is “spatially within” another Geometry. </td> <td> Boolean </td> </tr> <tr> <td> contains </td> <td> Returns 1 (TRUE) if this geometric object “spatially contains” another Geometry. </td> <td> Boolean </td> </tr> <tr> <td> overlaps </td> <td> Returns 1 (TRUE) if this geometric object “spatially overlaps” another Geometry. </td> <td> Boolean </td> </tr> <tr> <td> covers </td> <td> Alias for contains() </td> <td> Boolean </td> </tr> <tr> <td> coveredBy </td> <td> Alias for within() </td> <td> Boolean </td> </tr> <tr> <td> distance </td> <td> Returns the shortest distance between any two Points in the two geometric objects as calculated in the spatial reference system of this geometric object. Because the geometries are closed, it is possible to find a point on each geometric object involved, such that the distance between these 2 points is the returned distance between their geometric objects. </td> <td> Float </td> </tr> <tr> <td> hausdorffDistance </td> <td> See <a href="http://en.wikipedia.org/wiki/Hausdorff_distance" rel="nofollow" >http://en.wikipedia.org/wiki/Hausdorff_distance</a> </td> <td> Float </td> </tr> </table> <p> <b>Placeholders</b> </p> <p>These methods are part of the specification, but are not really supported by geoPHP. </p> <table> <tr> <td> <b>Method</b> </td> <td> <b>Description</b> </td> <td> <b>Returns</b> </td> </tr> <tr> <td> hasZ </td> <td> returns FALSE. geoPHP does not support Z values at the moment. </td> <td> Boolean </td> </tr> <tr> <td> is3D </td> <td> returns FALSE. geoPHP does not support 3D geometries at the moment. </td> <td> Boolean </td> </tr> <tr> <td> isMeasured </td> <td> returns FALSE. geoPHP does not yet support M values </td> <td> Boolean </td> </tr> <tr> <td> isEmpty </td> <td> returns FALSE. geoPHP does not yet support empty geometries </td> <td> Boolean </td> </tr> <tr> <td> coordinateDimension </td> <td> returns 2. geoPHP only supports 2-dimentional space </td> <td> Integer </td> </tr> <tr> <td> z </td> <td> returns NULL. geoPHP does not support Z values at the moment </td> <td> NULL </td> </tr> <tr> <td> m </td> <td> returns NULL. geoPHP does not support M values </td> <td> NULL </td> </tr> </table> </div> </div> </div> </div> </div> theme: jekyll-theme-cayman{ "$schema":"./vendor/phpbench/phpbench/phpbench.schema.json", "runner.bootstrap": "vendor/autoload.php", "runner.path": "tests/Benchmark", "storage.xml_storage_path": "build/phpbench/", "runner.php_config": { "memory_limit": "1G", "xdebug.mode": "none" }, "runner.iterations": 10, "runner.retry_threshold": 10, "runner.revs": 50, "runner.assert": "mode(variant.time.avg) as ms <= mode(baseline.time.avg) as ms +/- 8%" }[![Build Status](https://travis-ci.com/wgirhad/geoPHP.svg?branch=development)](https://travis-ci.com/wgirhad/geoPHP) [![Coverage Status](https://coveralls.io/repos/github/wgirhad/geoPHP/badge.svg?branch=development)](https://coveralls.io/github/wgirhad/geoPHP?branch=development) GeoPHP is a open-source native PHP library for doing geometry operations. It is a fork of famous [geoPHP](https://github.com/phayes/geoPHP) library by Patrick Hayes. ## Notice about this repo *This fork aims to unify the most relevant forks from the original work of phayes/geoPHP* ### Goals * Maintain compatibility with current [supported versions of PHP](https://www.php.net/supported-versions.php) * Keep the unit tests compatible with the latest PHPUnit * Merge contributions from other [geoPHP forks](https://github.com/phayes/geoPHP/forks), like [funiq](https://github.com/funiq/geoPHP), [swen100](https://github.com/swen100/geoPHP) and others It is written entirely in PHP and can therefore run on shared hosts. It can read and write a wide variety of formats: WKT (EWKT), WKB (EWKB), TWKB, GeoJSON, KML, GPX, and GeoRSS. It works with all Simple-Feature geometries (Point, LineString, Polygon, GeometryCollection etc.) and can be used to get centroids, bounding-boxes, area, and a wide variety of other useful information. GeoPHP also helpfully wraps the GEOS php extension so that applications can get a transparent performance increase when GEOS is installed on the server. When GEOS is installed, geoPHP also becomes fully compliant with the OpenGIS® Implementation Standard for Geographic information. With GEOS you get the full-set of openGIS functions in PHP like Union, IsWithin, Touches etc. This means that applications get a useful "core-set" of geometry operations that work in all environments, and an "extended-set"of operations for environments that have GEOS installed. See the _getting started_ section below for references and examples of everything that geoPHP can do. Forks and contributions are welcome. Please open [issues](https://github.com/wgirhad/geoPHP/issues), send [pull](https://github.com/wgirhad/geoPHP/pulls) requests and I will merge them into the main branch. ## Getting Started ### Example usage ```php <?php use \geoPHP\geoPHP; // Polygon WKT example $polygon = geoPHP::load('POLYGON((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2))','wkt'); $area = $polygon->area(); $centroid = $polygon->centroid(); $centX = $centroid->x(); $centY = $centroid->y(); print "This polygon has an area of ".$area." and a centroid with X=".$centX." and Y=".$centY; // MultiPoint json example print "<br/>"; $json = '{ "type": "MultiPoint", "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] }'; $multipoint = geoPHP::load($json, 'json'); $multipoint_points = $multipoint->getComponents(); $first_wkt = $multipoint_points[0]->out('wkt'); print "This multipoint has ".$multipoint->numGeometries()." points. The first point has a wkt representation of ".$first_wkt; ``` ### More Examples The Well Known Text (WKT) and Well Known Binary (WKB) support is ideal for integrating with MySQL's or PostGIS's spatial capability. Once you have SELECTed your data with `'AsText('geo_field')'` or `'AsBinary('geo_field')'`, you can put it straight into geoPHP (can be wkt or wkb, but must be the same as how you extracted it from your database): $geom = geoPHP::load($dbRow,'wkt'); You can collect multiple geometries into one (note that you must use wkt for this): $geom = geoPHP::load("GEOMETRYCOLLECTION(".$dbString1.",".$dbString2.")",'wkt'); Calling get components returns the sub-geometries within a geometry as an array. $geom2 = geoPHP::load("GEOMETRYCOLLECTION(LINESTRING(1 1,5 1,5 5,1 5,1 1),LINESTRING(2 2,2 3,3 3,3 2,2 2))"); $geomComponents = $geom2->getComponents(); //an array of the two linestring geometries $linestring1 = $geomComponents[0]->getComponents(); //an array of the first linestring's point geometries $linestring2 = $geomComponents[1]->getComponents(); echo $linestring1[0]->x() . ", " . $linestring1[0]->y(); //outputs '1, 1' An alternative is to use the `asArray()` method. Using the above geometry collection of two linestrings, $geometryArray = $geom2->asArray(); echo $geometryArray[0][0][0] . ", " . $geometryArray[0][0][1]; //outputs '1, 1' Clearly, more complex analysis is possible. echo $geom2->envelope()->area(); ### Working with PostGIS geoPHP, through it's EWKB adapter, has good integration with postGIS. Here's an example of reading and writing postGIS geometries ```php <?php use \geoPHP\geoPHP; $host = 'localhost'; $database = 'phayes'; $table = 'test'; $column = 'geom'; $user = 'phayes'; $pass = 'supersecret'; $connection = pg_connect("host=$host dbname=$database user=$user password=$pass"); // Working with PostGIS and Extended-WKB // ---------------------------- // Using asBinary and GeomFromWKB in PostGIS $result = pg_fetch_all(pg_query($connection, "SELECT asBinary($column) as geom FROM $table")); foreach ($result as $item) { $wkb = pg_unescape_bytea($item['geom']); // Make sure to unescape the hex blob $geom = geoPHP::load($wkb, 'ewkb'); // We now a full geoPHP Geometry object // Let's insert it back into the database $insert_string = pg_escape_bytea($geom->out('ewkb')); pg_query($connection, "INSERT INTO $table ($column) values (GeomFromWKB('$insert_string'))"); } // Using a direct SELECT and INSERTs in PostGIS without using wrapping functions $result = pg_fetch_all(pg_query($connection, "SELECT $column as geom FROM $table")); foreach ($result as $item) { $wkb = pack('H*',$item['geom']); // Unpacking the hex blob $geom = geoPHP::load($wkb, 'ewkb'); // We now have a geoPHP Geometry // To insert directly into postGIS we need to unpack the WKB $unpacked = unpack('H*', $geom->out('ewkb')); $insert_string = $unpacked[1]; pg_query($connection, "INSERT INTO $table ($column) values ('$insert_string')"); } ``` ## Documentation In progress… You can read the doc for original phayes/geoPHP at [geophp.net](https://geophp.net "GeoPHP homepage") ## Credit - Maintainer: Péter Báthory - Original author: Patrick Hayes Additional Contributors: * GeoMemes Research (<http://www.geomemes.com>) * HighWire Press (<http://www.highwire.org>) and GeoScienceWorld (<http://www.geoscienceworld.org>) * Arnaud Renevier (gisconverter.php) <https://github.com/arenevier/gisconverter.php> * Dave Tarc <https://github.com/dtarc> * Elliott Hunston (documentation) <https://github.com/ejh> This library is open-source and dual-licensed under both the Modified BSD License and GPLv2. Either license may be used at your option. Copyright (c) 2011, Patrick Hayes and contributors This program is dual-licensed under both the GPL version 2 (or later) and Modified BSD License. Either license may be used at your option. ------------------------------------------------------------------------------ Modified BSD License Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of the contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. { "_readme": [ "This file locks the dependencies of your project to a known state", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], "content-hash": "b1ffb6834f77c6a98e713fac9cb2cf3d", "packages": [], "packages-dev": [ { "name": "doctrine/annotations", "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", "shasum": "" }, "require": { "doctrine/lexer": "^2 || ^3", "ext-tokenizer": "*", "php": "^7.2 || ^8.0", "psr/cache": "^1 || ^2 || ^3" }, "require-dev": { "doctrine/cache": "^2.0", "doctrine/coding-standard": "^10", "phpstan/phpstan": "^1.8.0", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "symfony/cache": "^5.4 || ^6", "vimeo/psalm": "^4.10" }, "suggest": { "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" }, "type": "library", "autoload": { "psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com" }, { "name": "Roman Borschel", "email": "roman@code-factory.org" }, { "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" } ], "description": "Docblock Annotations Parser", "homepage": "https://www.doctrine-project.org/projects/annotations.html", "keywords": [ "annotations", "docblock", "parser" ], "support": { "issues": "https://github.com/doctrine/annotations/issues", "source": "https://github.com/doctrine/annotations/tree/2.0.1" }, "time": "2023-02-02T22:02:53+00:00" }, { "name": "doctrine/instantiator", "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "shasum": "" }, "require": { "php": "^8.1" }, "require-dev": { "doctrine/coding-standard": "^11", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^1.2", "phpstan/phpstan": "^1.9.4", "phpstan/phpstan-phpunit": "^1.3", "phpunit/phpunit": "^9.5.27", "vimeo/psalm": "^5.4" }, "type": "library", "autoload": { "psr-4": { "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Marco Pivetta", "email": "ocramius@gmail.com", "homepage": "https://ocramius.github.io/" } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", "homepage": "https://www.doctrine-project.org/projects/instantiator.html", "keywords": [ "constructor", "instantiate" ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", "source": "https://github.com/doctrine/instantiator/tree/2.0.0" }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", "type": "custom" }, { "url": "https://www.patreon.com/phpdoctrine", "type": "patreon" }, { "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", "type": "tidelift" } ], "time": "2022-12-30T00:23:10+00:00" }, { "name": "doctrine/lexer", "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", "reference": "84a527db05647743d50373e0ec53a152f2cde568" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568", "reference": "84a527db05647743d50373e0ec53a152f2cde568", "shasum": "" }, "require": { "php": "^8.1" }, "require-dev": { "doctrine/coding-standard": "^10", "phpstan/phpstan": "^1.9", "phpunit/phpunit": "^9.5", "psalm/plugin-phpunit": "^0.18.3", "vimeo/psalm": "^5.0" }, "type": "library", "autoload": { "psr-4": { "Doctrine\\Common\\Lexer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com" }, { "name": "Roman Borschel", "email": "roman@code-factory.org" }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" } ], "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", "homepage": "https://www.doctrine-project.org/projects/lexer.html", "keywords": [ "annotations", "docblock", "lexer", "parser", "php" ], "support": { "issues": "https://github.com/doctrine/lexer/issues", "source": "https://github.com/doctrine/lexer/tree/3.0.0" }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", "type": "custom" }, { "url": "https://www.patreon.com/phpdoctrine", "type": "patreon" }, { "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", "type": "tidelift" } ], "time": "2022-12-15T16:57:16+00:00" }, { "name": "guzzlehttp/guzzle", "version": "7.5.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba", "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba", "shasum": "" }, "require": { "ext-json": "*", "guzzlehttp/promises": "^1.5", "guzzlehttp/psr7": "^1.9 || ^2.4", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" }, "provide": { "psr/http-client-implementation": "1.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.1", "ext-curl": "*", "php-http/client-integration-tests": "^3.0", "phpunit/phpunit": "^8.5.29 || ^9.5.23", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { "ext-curl": "Required for CURL handler support", "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "bamarni-bin": { "bin-links": true, "forward-command": false }, "branch-alias": { "dev-master": "7.5-dev" } }, "autoload": { "files": [ "src/functions_include.php" ], "psr-4": { "GuzzleHttp\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Graham Campbell", "email": "hello@gjcampbell.co.uk", "homepage": "https://github.com/GrahamCampbell" }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, { "name": "Jeremy Lindblom", "email": "jeremeamia@gmail.com", "homepage": "https://github.com/jeremeamia" }, { "name": "George Mponos", "email": "gmponos@gmail.com", "homepage": "https://github.com/gmponos" }, { "name": "Tobias Nyholm", "email": "tobias.nyholm@gmail.com", "homepage": "https://github.com/Nyholm" }, { "name": "Márk Sági-Kazár", "email": "mark.sagikazar@gmail.com", "homepage": "https://github.com/sagikazarmark" }, { "name": "Tobias Schultze", "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" } ], "description": "Guzzle is a PHP HTTP client library", "keywords": [ "client", "curl", "framework", "http", "http client", "psr-18", "psr-7", "rest", "web service" ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", "source": "https://github.com/guzzle/guzzle/tree/7.5.0" }, "funding": [ { "url": "https://github.com/GrahamCampbell", "type": "github" }, { "url": "https://github.com/Nyholm", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", "type": "tidelift" } ], "time": "2022-08-28T15:39:27+00:00" }, { "name": "guzzlehttp/promises", "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", "reference": "b94b2807d85443f9719887892882d0329d1e2598" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598", "reference": "b94b2807d85443f9719887892882d0329d1e2598", "shasum": "" }, "require": { "php": ">=5.5" }, "require-dev": { "symfony/phpunit-bridge": "^4.4 || ^5.1" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.5-dev" } }, "autoload": { "files": [ "src/functions_include.php" ], "psr-4": { "GuzzleHttp\\Promise\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Graham Campbell", "email": "hello@gjcampbell.co.uk", "homepage": "https://github.com/GrahamCampbell" }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, { "name": "Tobias Nyholm", "email": "tobias.nyholm@gmail.com", "homepage": "https://github.com/Nyholm" }, { "name": "Tobias Schultze", "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" } ], "description": "Guzzle promises library", "keywords": [ "promise" ], "support": { "issues": "https://github.com/guzzle/promises/issues", "source": "https://github.com/guzzle/promises/tree/1.5.2" }, "funding": [ { "url": "https://github.com/GrahamCampbell", "type": "github" }, { "url": "https://github.com/Nyholm", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", "type": "tidelift" } ], "time": "2022-08-28T14:55:35+00:00" }, { "name": "guzzlehttp/psr7", "version": "2.4.3", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", "reference": "67c26b443f348a51926030c83481b85718457d3d" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/psr7/zipball/67c26b443f348a51926030c83481b85718457d3d", "reference": "67c26b443f348a51926030c83481b85718457d3d", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0", "ralouphie/getallheaders": "^3.0" }, "provide": { "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.1", "http-interop/http-factory-tests": "^0.9", "phpunit/phpunit": "^8.5.29 || ^9.5.23" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", "extra": { "bamarni-bin": { "bin-links": true, "forward-command": false }, "branch-alias": { "dev-master": "2.4-dev" } }, "autoload": { "psr-4": { "GuzzleHttp\\Psr7\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Graham Campbell", "email": "hello@gjcampbell.co.uk", "homepage": "https://github.com/GrahamCampbell" }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, { "name": "George Mponos", "email": "gmponos@gmail.com", "homepage": "https://github.com/gmponos" }, { "name": "Tobias Nyholm", "email": "tobias.nyholm@gmail.com", "homepage": "https://github.com/Nyholm" }, { "name": "Márk Sági-Kazár", "email": "mark.sagikazar@gmail.com", "homepage": "https://github.com/sagikazarmark" }, { "name": "Tobias Schultze", "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" }, { "name": "Márk Sági-Kazár", "email": "mark.sagikazar@gmail.com", "homepage": "https://sagikazarmark.hu" } ], "description": "PSR-7 message implementation that also provides common utility methods", "keywords": [ "http", "message", "psr-7", "request", "response", "stream", "uri", "url" ], "support": { "issues": "https://github.com/guzzle/psr7/issues", "source": "https://github.com/guzzle/psr7/tree/2.4.3" }, "funding": [ { "url": "https://github.com/GrahamCampbell", "type": "github" }, { "url": "https://github.com/Nyholm", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", "type": "tidelift" } ], "time": "2022-10-26T14:07:24+00:00" }, { "name": "myclabs/deep-copy", "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "conflict": { "doctrine/collections": "<1.6.8", "doctrine/common": "<2.13.3 || >=3,<3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", "autoload": { "files": [ "src/DeepCopy/deep_copy.php" ], "psr-4": { "DeepCopy\\": "src/DeepCopy/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "description": "Create deep copies (clones) of your objects", "keywords": [ "clone", "copy", "duplicate", "object", "object graph" ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" }, "funding": [ { "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", "type": "tidelift" } ], "time": "2022-03-03T13:19:32+00:00" }, { "name": "nikic/php-parser", "version": "v4.15.3", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", "shasum": "" }, "require": { "ext-tokenizer": "*", "php": ">=7.0" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" }, "bin": [ "bin/php-parse" ], "type": "library", "extra": { "branch-alias": { "dev-master": "4.9-dev" } }, "autoload": { "psr-4": { "PhpParser\\": "lib/PhpParser" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Nikita Popov" } ], "description": "A PHP parser written in PHP", "keywords": [ "parser", "php" ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" }, "time": "2023-01-16T22:05:37+00:00" }, { "name": "phar-io/manifest", "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", "reference": "97803eca37d319dfa7826cc2437fc020857acb53" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", "reference": "97803eca37d319dfa7826cc2437fc020857acb53", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", "ext-xmlwriter": "*", "phar-io/version": "^3.0.1", "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Arne Blankerts", "email": "arne@blankerts.de", "role": "Developer" }, { "name": "Sebastian Heuer", "email": "sebastian@phpeople.de", "role": "Developer" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "Developer" } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", "source": "https://github.com/phar-io/manifest/tree/2.0.3" }, "time": "2021-07-20T11:28:43+00:00" }, { "name": "phar-io/version", "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Arne Blankerts", "email": "arne@blankerts.de", "role": "Developer" }, { "name": "Sebastian Heuer", "email": "sebastian@phpeople.de", "role": "Developer" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "Developer" } ], "description": "Library for handling version information and constraints", "support": { "issues": "https://github.com/phar-io/version/issues", "source": "https://github.com/phar-io/version/tree/3.2.1" }, "time": "2022-02-21T01:04:05+00:00" }, { "name": "php-coveralls/php-coveralls", "version": "v2.5.3", "source": { "type": "git", "url": "https://github.com/php-coveralls/php-coveralls.git", "reference": "9d8243bbf0e053333692857c98fab7cfba0d60a9" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-coveralls/php-coveralls/zipball/9d8243bbf0e053333692857c98fab7cfba0d60a9", "reference": "9d8243bbf0e053333692857c98fab7cfba0d60a9", "shasum": "" }, "require": { "ext-json": "*", "ext-simplexml": "*", "guzzlehttp/guzzle": "^6.0 || ^7.0", "php": "^5.5 || ^7.0 || ^8.0", "psr/log": "^1.0 || ^2.0", "symfony/config": "^2.1 || ^3.0 || ^4.0 || ^5.0 || ^6.0", "symfony/console": "^2.1 || ^3.0 || ^4.0 || ^5.0 || ^6.0", "symfony/stopwatch": "^2.0 || ^3.0 || ^4.0 || ^5.0 || ^6.0", "symfony/yaml": "^2.0.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.4.3 || ^6.0 || ^7.0 || >=8.0 <8.5.29 || >=9.0 <9.5.23", "sanmai/phpunit-legacy-adapter": "^6.1 || ^8.0" }, "suggest": { "symfony/http-kernel": "Allows Symfony integration" }, "bin": [ "bin/php-coveralls" ], "type": "library", "autoload": { "psr-4": { "PhpCoveralls\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Kitamura Satoshi", "email": "with.no.parachute@gmail.com", "homepage": "https://www.facebook.com/satooshi.jp", "role": "Original creator" }, { "name": "Takashi Matsuo", "email": "tmatsuo@google.com" }, { "name": "Google Inc" }, { "name": "Dariusz Ruminski", "email": "dariusz.ruminski@gmail.com", "homepage": "https://github.com/keradus" }, { "name": "Contributors", "homepage": "https://github.com/php-coveralls/php-coveralls/graphs/contributors" } ], "description": "PHP client library for Coveralls API", "homepage": "https://github.com/php-coveralls/php-coveralls", "keywords": [ "ci", "coverage", "github", "test" ], "support": { "issues": "https://github.com/php-coveralls/php-coveralls/issues", "source": "https://github.com/php-coveralls/php-coveralls/tree/v2.5.3" }, "time": "2022-09-12T20:47:09+00:00" }, { "name": "phpbench/container", "version": "2.2.1", "source": { "type": "git", "url": "https://github.com/phpbench/container.git", "reference": "6d555ff7174fca13f9b1ec0b4a089ed41d0ab392" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpbench/container/zipball/6d555ff7174fca13f9b1ec0b4a089ed41d0ab392", "reference": "6d555ff7174fca13f9b1ec0b4a089ed41d0ab392", "shasum": "" }, "require": { "psr/container": "^1.0|^2.0", "symfony/options-resolver": "^4.2 || ^5.0 || ^6.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.16", "phpstan/phpstan": "^0.12.52", "phpunit/phpunit": "^8" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.x-dev" } }, "autoload": { "psr-4": { "PhpBench\\DependencyInjection\\": "lib/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Daniel Leech", "email": "daniel@dantleech.com" } ], "description": "Simple, configurable, service container.", "support": { "issues": "https://github.com/phpbench/container/issues", "source": "https://github.com/phpbench/container/tree/2.2.1" }, "time": "2022-01-25T10:17:35+00:00" }, { "name": "phpbench/dom", "version": "0.3.2", "source": { "type": "git", "url": "https://github.com/phpbench/dom.git", "reference": "b013b717832ddbaadf2a40984b04bc66af9a7110" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpbench/dom/zipball/b013b717832ddbaadf2a40984b04bc66af9a7110", "reference": "b013b717832ddbaadf2a40984b04bc66af9a7110", "shasum": "" }, "require": { "ext-dom": "*", "php": "^7.2||^8.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.18", "phpstan/phpstan": "^0.12.83", "phpunit/phpunit": "^8.0||^9.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0-dev" } }, "autoload": { "psr-4": { "PhpBench\\Dom\\": "lib/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Daniel Leech", "email": "daniel@dantleech.com" } ], "description": "DOM wrapper to simplify working with the PHP DOM implementation", "support": { "issues": "https://github.com/phpbench/dom/issues", "source": "https://github.com/phpbench/dom/tree/0.3.2" }, "time": "2021-09-24T15:26:07+00:00" }, { "name": "phpbench/phpbench", "version": "1.2.8", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", "reference": "3f7b3c200f86727de7a14bde94adb68a88e1bafc" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpbench/phpbench/zipball/3f7b3c200f86727de7a14bde94adb68a88e1bafc", "reference": "3f7b3c200f86727de7a14bde94adb68a88e1bafc", "shasum": "" }, "require": { "doctrine/annotations": "^1.13 || ^2.0", "ext-dom": "*", "ext-json": "*", "ext-pcre": "*", "ext-reflection": "*", "ext-spl": "*", "ext-tokenizer": "*", "php": "^7.3 || ^8.0", "phpbench/container": "^2.1", "phpbench/dom": "~0.3.1", "psr/log": "^1.1 || ^2.0 || ^3.0", "seld/jsonlint": "^1.1", "symfony/console": "^4.2 || ^5.0 || ^6.0", "symfony/filesystem": "^4.2 || ^5.0 || ^6.0", "symfony/finder": "^4.2 || ^5.0 || ^6.0", "symfony/options-resolver": "^4.2 || ^5.0 || ^6.0", "symfony/process": "^4.2 || ^5.0 || ^6.0", "webmozart/glob": "^4.6" }, "require-dev": { "dantleech/invoke": "^2.0", "friendsofphp/php-cs-fixer": "^3.0", "jangregor/phpstan-prophecy": "^1.0", "phpspec/prophecy": "^1.12", "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.0", "phpstan/phpstan-phpunit": "^1.0", "phpunit/phpunit": "^8.5.8 || ^9.0", "symfony/error-handler": "^5.2 || ^6.0", "symfony/var-dumper": "^4.0 || ^5.0 || ^6.0" }, "suggest": { "ext-xdebug": "For Xdebug profiling extension." }, "bin": [ "bin/phpbench" ], "type": "library", "extra": { "branch-alias": { "dev-master": "1.2-dev" } }, "autoload": { "files": [ "lib/Report/Func/functions.php" ], "psr-4": { "PhpBench\\": "lib/", "PhpBench\\Extensions\\XDebug\\": "extensions/xdebug/lib/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Daniel Leech", "email": "daniel@dantleech.com" } ], "description": "PHP Benchmarking Framework", "support": { "issues": "https://github.com/phpbench/phpbench/issues", "source": "https://github.com/phpbench/phpbench/tree/1.2.8" }, "funding": [ { "url": "https://github.com/dantleech", "type": "github" } ], "time": "2023-01-14T13:08:42+00:00" }, { "name": "phpstan/phpstan", "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", "reference": "1cd5fc530a8b68702f3733ad64294b2a39564198" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1cd5fc530a8b68702f3733ad64294b2a39564198", "reference": "1cd5fc530a8b68702f3733ad64294b2a39564198", "shasum": "" }, "require": { "php": "^7.2|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" }, "bin": [ "phpstan", "phpstan.phar" ], "type": "library", "autoload": { "files": [ "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "description": "PHPStan - PHP Static Analysis Tool", "keywords": [ "dev", "static analysis" ], "support": { "issues": "https://github.com/phpstan/phpstan/issues", "source": "https://github.com/phpstan/phpstan/tree/1.10.1" }, "funding": [ { "url": "https://github.com/ondrejmirtes", "type": "github" }, { "url": "https://github.com/phpstan", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", "type": "tidelift" } ], "time": "2023-02-21T21:57:23+00:00" }, { "name": "phpunit/php-code-coverage", "version": "9.2.24", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2cf940ebc6355a9d430462811b5aaa308b174bed", "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", "nikic/php-parser": "^4.14", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", "sebastian/code-unit-reverse-lookup": "^2.0.2", "sebastian/complexity": "^2.0", "sebastian/environment": "^5.1.2", "sebastian/lines-of-code": "^1.0.3", "sebastian/version": "^3.0.1", "theseer/tokenizer": "^1.2.0" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "suggest": { "ext-pcov": "*", "ext-xdebug": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "9.2-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", "homepage": "https://github.com/sebastianbergmann/php-code-coverage", "keywords": [ "coverage", "testing", "xunit" ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.24" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-01-26T08:26:55+00:00" }, { "name": "phpunit/php-file-iterator", "version": "3.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "FilterIterator implementation that filters files based on a list of suffixes.", "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", "keywords": [ "filesystem", "iterator" ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2021-12-02T12:48:52+00:00" }, { "name": "phpunit/php-invoker", "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "ext-pcntl": "*", "phpunit/phpunit": "^9.3" }, "suggest": { "ext-pcntl": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.1-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Invoke callables with a timeout", "homepage": "https://github.com/sebastianbergmann/php-invoker/", "keywords": [ "process" ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T05:58:55+00:00" }, { "name": "phpunit/php-text-template", "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Simple template engine.", "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ "template" ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T05:33:50+00:00" }, { "name": "phpunit/php-timer", "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "5.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Utility class for timing", "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ "timer" ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:16:10+00:00" }, { "name": "phpunit/phpunit", "version": "9.6.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", "reference": "e7b1615e3e887d6c719121c6d4a44b0ab9645555" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e7b1615e3e887d6c719121c6d4a44b0ab9645555", "reference": "e7b1615e3e887d6c719121c6d4a44b0ab9645555", "shasum": "" }, "require": { "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", "myclabs/deep-copy": "^1.10.1", "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", "php": ">=7.3", "phpunit/php-code-coverage": "^9.2.13", "phpunit/php-file-iterator": "^3.0.5", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.3", "phpunit/php-timer": "^5.0.2", "sebastian/cli-parser": "^1.0.1", "sebastian/code-unit": "^1.0.6", "sebastian/comparator": "^4.0.8", "sebastian/diff": "^4.0.3", "sebastian/environment": "^5.1.3", "sebastian/exporter": "^4.0.5", "sebastian/global-state": "^5.0.1", "sebastian/object-enumerator": "^4.0.3", "sebastian/resource-operations": "^3.0.3", "sebastian/type": "^3.2", "sebastian/version": "^3.0.2" }, "suggest": { "ext-soap": "*", "ext-xdebug": "*" }, "bin": [ "phpunit" ], "type": "library", "extra": { "branch-alias": { "dev-master": "9.6-dev" } }, "autoload": { "files": [ "src/Framework/Assert/Functions.php" ], "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "The PHP Unit Testing framework.", "homepage": "https://phpunit.de/", "keywords": [ "phpunit", "testing", "xunit" ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.3" }, "funding": [ { "url": "https://phpunit.de/sponsors.html", "type": "custom" }, { "url": "https://github.com/sebastianbergmann", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], "time": "2023-02-04T13:37:15+00:00" }, { "name": "psr/cache", "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/cache.git", "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", "shasum": "" }, "require": { "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\Cache\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for caching libraries", "keywords": [ "cache", "psr", "psr-6" ], "support": { "source": "https://github.com/php-fig/cache/tree/3.0.0" }, "time": "2021-02-03T23:26:27+00:00" }, { "name": "psr/container", "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, "require": { "php": ">=7.4.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "https://www.php-fig.org/" } ], "description": "Common Container Interface (PHP FIG PSR-11)", "homepage": "https://github.com/php-fig/container", "keywords": [ "PSR-11", "container", "container-interface", "container-interop", "psr" ], "support": { "issues": "https://github.com/php-fig/container/issues", "source": "https://github.com/php-fig/container/tree/2.0.2" }, "time": "2021-11-05T16:47:00+00:00" }, { "name": "psr/http-client", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-client.git", "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", "shasum": "" }, "require": { "php": "^7.0 || ^8.0", "psr/http-message": "^1.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\Http\\Client\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "http://www.php-fig.org/" } ], "description": "Common interface for HTTP clients", "homepage": "https://github.com/php-fig/http-client", "keywords": [ "http", "http-client", "psr", "psr-18" ], "support": { "source": "https://github.com/php-fig/http-client/tree/master" }, "time": "2020-06-29T06:28:15+00:00" }, { "name": "psr/http-factory", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-factory.git", "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", "shasum": "" }, "require": { "php": ">=7.0.0", "psr/http-message": "^1.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "http://www.php-fig.org/" } ], "description": "Common interfaces for PSR-7 HTTP message factories", "keywords": [ "factory", "http", "message", "psr", "psr-17", "psr-7", "request", "response" ], "support": { "source": "https://github.com/php-fig/http-factory/tree/master" }, "time": "2019-04-30T12:38:16+00:00" }, { "name": "psr/http-message", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", "shasum": "" }, "require": { "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "http://www.php-fig.org/" } ], "description": "Common interface for HTTP messages", "homepage": "https://github.com/php-fig/http-message", "keywords": [ "http", "http-message", "psr", "psr-7", "request", "response" ], "support": { "source": "https://github.com/php-fig/http-message/tree/master" }, "time": "2016-08-06T14:39:51+00:00" }, { "name": "psr/log", "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/log/zipball/ef29f6d262798707a9edd554e2b82517ef3a9376", "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376", "shasum": "" }, "require": { "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for logging libraries", "homepage": "https://github.com/php-fig/log", "keywords": [ "log", "psr", "psr-3" ], "support": { "source": "https://github.com/php-fig/log/tree/2.0.0" }, "time": "2021-07-14T16:41:46+00:00" }, { "name": "ralouphie/getallheaders", "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/ralouphie/getallheaders.git", "reference": "120b605dfeb996808c31b6477290a714d356e822" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", "reference": "120b605dfeb996808c31b6477290a714d356e822", "shasum": "" }, "require": { "php": ">=5.6" }, "require-dev": { "php-coveralls/php-coveralls": "^2.1", "phpunit/phpunit": "^5 || ^6.5" }, "type": "library", "autoload": { "files": [ "src/getallheaders.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Ralph Khattar", "email": "ralph.khattar@gmail.com" } ], "description": "A polyfill for getallheaders.", "support": { "issues": "https://github.com/ralouphie/getallheaders/issues", "source": "https://github.com/ralouphie/getallheaders/tree/develop" }, "time": "2019-03-08T08:55:37+00:00" }, { "name": "sebastian/cli-parser", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library for parsing CLI options", "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T06:08:49+00:00" }, { "name": "sebastian/code-unit", "version": "1.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit.git", "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Collection of value objects that represent the PHP code units", "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { "issues": "https://github.com/sebastianbergmann/code-unit/issues", "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:08:54+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T05:30:19+00:00" }, { "name": "sebastian/comparator", "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", "reference": "fa0f136dd2334583309d32b62544682ee972b51a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", "reference": "fa0f136dd2334583309d32b62544682ee972b51a", "shasum": "" }, "require": { "php": ">=7.3", "sebastian/diff": "^4.0", "sebastian/exporter": "^4.0" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Volker Dusch", "email": "github@wallbash.com" }, { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" } ], "description": "Provides the functionality to compare PHP values for equality", "homepage": "https://github.com/sebastianbergmann/comparator", "keywords": [ "comparator", "compare", "equality" ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2022-09-14T12:41:17+00:00" }, { "name": "sebastian/complexity", "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", "shasum": "" }, "require": { "nikic/php-parser": "^4.7", "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library for calculating the complexity of PHP code units", "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T15:52:27+00:00" }, { "name": "sebastian/diff", "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3", "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Kore Nordmann", "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ "diff", "udiff", "unidiff", "unified diff" ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:10:38+00:00" }, { "name": "sebastian/environment", "version": "5.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "suggest": { "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "5.1-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Provides functionality to handle HHVM/PHP environments", "homepage": "http://www.github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", "hhvm" ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-02-03T06:03:51+00:00" }, { "name": "sebastian/exporter", "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", "shasum": "" }, "require": { "php": ">=7.3", "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-mbstring": "*", "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Volker Dusch", "email": "github@wallbash.com" }, { "name": "Adam Harvey", "email": "aharvey@php.net" }, { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2022-09-14T06:03:37+00:00" }, { "name": "sebastian/global-state", "version": "5.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", "shasum": "" }, "require": { "php": ">=7.3", "sebastian/object-reflector": "^2.0", "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-dom": "*", "phpunit/phpunit": "^9.3" }, "suggest": { "ext-uopz": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "5.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Snapshotting of global state", "homepage": "http://www.github.com/sebastianbergmann/global-state", "keywords": [ "global state" ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2022-02-14T08:28:10+00:00" }, { "name": "sebastian/lines-of-code", "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", "shasum": "" }, "require": { "nikic/php-parser": "^4.6", "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library for counting the lines of code in PHP source code", "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-11-28T06:42:11+00:00" }, { "name": "sebastian/object-enumerator", "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", "shasum": "" }, "require": { "php": ">=7.3", "sebastian/object-reflector": "^2.0", "sebastian/recursion-context": "^4.0" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:12:34+00:00" }, { "name": "sebastian/object-reflector", "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:14:26+00:00" }, { "name": "sebastian/recursion-context", "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Adam Harvey", "email": "aharvey@php.net" } ], "description": "Provides functionality to recursively process PHP variables", "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-02-03T06:07:39+00:00" }, { "name": "sebastian/resource-operations", "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { "issues": "https://github.com/sebastianbergmann/resource-operations/issues", "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T06:45:17+00:00" }, { "name": "sebastian/type", "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.5" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.2-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-02-03T06:13:03+00:00" }, { "name": "sebastian/version", "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { "php": ">=7.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T06:39:44+00:00" }, { "name": "seld/jsonlint", "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", "reference": "4211420d25eba80712bff236a98960ef68b866b7" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/4211420d25eba80712bff236a98960ef68b866b7", "reference": "4211420d25eba80712bff236a98960ef68b866b7", "shasum": "" }, "require": { "php": "^5.3 || ^7.0 || ^8.0" }, "require-dev": { "phpstan/phpstan": "^1.5", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" }, "bin": [ "bin/jsonlint" ], "type": "library", "autoload": { "psr-4": { "Seld\\JsonLint\\": "src/Seld/JsonLint/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", "homepage": "http://seld.be" } ], "description": "JSON Linter", "keywords": [ "json", "linter", "parser", "validator" ], "support": { "issues": "https://github.com/Seldaek/jsonlint/issues", "source": "https://github.com/Seldaek/jsonlint/tree/1.9.0" }, "funding": [ { "url": "https://github.com/Seldaek", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", "type": "tidelift" } ], "time": "2022-04-01T13:37:23+00:00" }, { "name": "squizlabs/php_codesniffer", "version": "3.7.2", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", "shasum": "" }, "require": { "ext-simplexml": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", "php": ">=5.4.0" }, "require-dev": { "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, "bin": [ "bin/phpcs", "bin/phpcbf" ], "type": "library", "extra": { "branch-alias": { "dev-master": "3.x-dev" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Greg Sherwood", "role": "lead" } ], "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", "keywords": [ "phpcs", "standards", "static analysis" ], "support": { "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", "source": "https://github.com/squizlabs/PHP_CodeSniffer", "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" }, "time": "2023-02-22T23:07:41+00:00" }, { "name": "symfony/config", "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/config.git", "reference": "f31b3c78a3650157188a240695e688d6a182aa91" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/config/zipball/f31b3c78a3650157188a240695e688d6a182aa91", "reference": "f31b3c78a3650157188a240695e688d6a182aa91", "shasum": "" }, "require": { "php": ">=8.1", "symfony/deprecation-contracts": "^2.1|^3", "symfony/filesystem": "^5.4|^6.0", "symfony/polyfill-ctype": "~1.8" }, "conflict": { "symfony/finder": "<5.4" }, "require-dev": { "symfony/event-dispatcher": "^5.4|^6.0", "symfony/finder": "^5.4|^6.0", "symfony/messenger": "^5.4|^6.0", "symfony/service-contracts": "^1.1|^2|^3", "symfony/yaml": "^5.4|^6.0" }, "suggest": { "symfony/yaml": "To use the yaml reference dumper" }, "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\Config\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { "source": "https://github.com/symfony/config/tree/v6.2.5" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2023-01-09T04:38:22+00:00" }, { "name": "symfony/console", "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", "reference": "3e294254f2191762c1d137aed4b94e966965e985" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/console/zipball/3e294254f2191762c1d137aed4b94e966965e985", "reference": "3e294254f2191762c1d137aed4b94e966965e985", "shasum": "" }, "require": { "php": ">=8.1", "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^1.1|^2|^3", "symfony/string": "^5.4|^6.0" }, "conflict": { "symfony/dependency-injection": "<5.4", "symfony/dotenv": "<5.4", "symfony/event-dispatcher": "<5.4", "symfony/lock": "<5.4", "symfony/process": "<5.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", "symfony/config": "^5.4|^6.0", "symfony/dependency-injection": "^5.4|^6.0", "symfony/event-dispatcher": "^5.4|^6.0", "symfony/lock": "^5.4|^6.0", "symfony/process": "^5.4|^6.0", "symfony/var-dumper": "^5.4|^6.0" }, "suggest": { "psr/log": "For using the console logger", "symfony/event-dispatcher": "", "symfony/lock": "", "symfony/process": "" }, "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\Console\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", "keywords": [ "cli", "command line", "console", "terminal" ], "support": { "source": "https://github.com/symfony/console/tree/v6.2.5" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2023-01-01T08:38:09+00:00" }, { "name": "symfony/deprecation-contracts", "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3", "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3", "shasum": "" }, "require": { "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", "url": "https://github.com/symfony/contracts" } }, "autoload": { "files": [ "function.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/filesystem", "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", "reference": "e59e8a4006afd7f5654786a83b4fcb8da98f4593" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/filesystem/zipball/e59e8a4006afd7f5654786a83b4fcb8da98f4593", "reference": "e59e8a4006afd7f5654786a83b4fcb8da98f4593", "shasum": "" }, "require": { "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\Filesystem\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { "source": "https://github.com/symfony/filesystem/tree/v6.2.5" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2023-01-20T17:45:48+00:00" }, { "name": "symfony/finder", "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", "reference": "c90dc446976a612e3312a97a6ec0069ab0c2099c" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/finder/zipball/c90dc446976a612e3312a97a6ec0069ab0c2099c", "reference": "c90dc446976a612e3312a97a6ec0069ab0c2099c", "shasum": "" }, "require": { "php": ">=8.1" }, "require-dev": { "symfony/filesystem": "^6.0" }, "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\Finder\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { "source": "https://github.com/symfony/finder/tree/v6.2.5" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2023-01-20T17:45:48+00:00" }, { "name": "symfony/options-resolver", "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", "reference": "e8324d44f5af99ec2ccec849934a242f64458f86" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/options-resolver/zipball/e8324d44f5af99ec2ccec849934a242f64458f86", "reference": "e8324d44f5af99ec2ccec849934a242f64458f86", "shasum": "" }, "require": { "php": ">=8.1", "symfony/deprecation-contracts": "^2.1|^3" }, "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\OptionsResolver\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Provides an improved replacement for the array_replace PHP function", "homepage": "https://symfony.com", "keywords": [ "config", "configuration", "options" ], "support": { "source": "https://github.com/symfony/options-resolver/tree/v6.2.5" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2023-01-01T08:38:09+00:00" }, { "name": "symfony/polyfill-ctype", "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", "shasum": "" }, "require": { "php": ">=7.1" }, "provide": { "ext-ctype": "*" }, "suggest": { "ext-ctype": "For best performance" }, "type": "library", "extra": { "branch-alias": { "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "autoload": { "files": [ "bootstrap.php" ], "psr-4": { "Symfony\\Polyfill\\Ctype\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Gert de Pagter", "email": "BackEndTea@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "ctype", "polyfill", "portable" ], "support": { "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-intl-grapheme", "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", "reference": "511a08c03c1960e08a883f4cffcacd219b758354" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", "reference": "511a08c03c1960e08a883f4cffcacd219b758354", "shasum": "" }, "require": { "php": ">=7.1" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { "branch-alias": { "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "autoload": { "files": [ "bootstrap.php" ], "psr-4": { "Symfony\\Polyfill\\Intl\\Grapheme\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for intl's grapheme_* functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "grapheme", "intl", "polyfill", "portable", "shim" ], "support": { "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-intl-normalizer", "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", "shasum": "" }, "require": { "php": ">=7.1" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { "branch-alias": { "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "autoload": { "files": [ "bootstrap.php" ], "psr-4": { "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, "classmap": [ "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for intl's Normalizer class and related functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "intl", "normalizer", "polyfill", "portable", "shim" ], "support": { "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-mbstring", "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "shasum": "" }, "require": { "php": ">=7.1" }, "provide": { "ext-mbstring": "*" }, "suggest": { "ext-mbstring": "For best performance" }, "type": "library", "extra": { "branch-alias": { "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "autoload": { "files": [ "bootstrap.php" ], "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", "keywords": [ "compatibility", "mbstring", "polyfill", "portable", "shim" ], "support": { "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/process", "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", "reference": "9ead139f63dfa38c4e4a9049cc64a8b2748c83b7" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/process/zipball/9ead139f63dfa38c4e4a9049cc64a8b2748c83b7", "reference": "9ead139f63dfa38c4e4a9049cc64a8b2748c83b7", "shasum": "" }, "require": { "php": ">=8.1" }, "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\Process\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { "source": "https://github.com/symfony/process/tree/v6.2.5" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2023-01-01T08:38:09+00:00" }, { "name": "symfony/service-contracts", "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/service-contracts/zipball/aac98028c69df04ee77eb69b96b86ee51fbf4b75", "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75", "shasum": "" }, "require": { "php": ">=8.1", "psr/container": "^2.0" }, "conflict": { "ext-psr": "<1.1|>=2" }, "suggest": { "symfony/service-implementation": "" }, "type": "library", "extra": { "branch-alias": { "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", "url": "https://github.com/symfony/contracts" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" }, "exclude-from-classmap": [ "/Test/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Generic abstractions related to writing services", "homepage": "https://symfony.com", "keywords": [ "abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards" ], "support": { "source": "https://github.com/symfony/service-contracts/tree/v3.2.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/stopwatch", "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", "reference": "00b6ac156aacffc53487c930e0ab14587a6607f6" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/stopwatch/zipball/00b6ac156aacffc53487c930e0ab14587a6607f6", "reference": "00b6ac156aacffc53487c930e0ab14587a6607f6", "shasum": "" }, "require": { "php": ">=8.1", "symfony/service-contracts": "^1|^2|^3" }, "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\Stopwatch\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { "source": "https://github.com/symfony/stopwatch/tree/v6.2.5" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2023-01-01T08:36:55+00:00" }, { "name": "symfony/string", "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/string.git", "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/string/zipball/b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", "shasum": "" }, "require": { "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { "symfony/translation-contracts": "<2.0" }, "require-dev": { "symfony/error-handler": "^5.4|^6.0", "symfony/http-client": "^5.4|^6.0", "symfony/intl": "^6.2", "symfony/translation-contracts": "^2.0|^3.0", "symfony/var-exporter": "^5.4|^6.0" }, "type": "library", "autoload": { "files": [ "Resources/functions.php" ], "psr-4": { "Symfony\\Component\\String\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", "homepage": "https://symfony.com", "keywords": [ "grapheme", "i18n", "string", "unicode", "utf-8", "utf8" ], "support": { "source": "https://github.com/symfony/string/tree/v6.2.5" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2023-01-01T08:38:09+00:00" }, { "name": "symfony/yaml", "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", "reference": "2bbfbdacc8a15574f8440c4838ce0d7bb6c86b19" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/yaml/zipball/2bbfbdacc8a15574f8440c4838ce0d7bb6c86b19", "reference": "2bbfbdacc8a15574f8440c4838ce0d7bb6c86b19", "shasum": "" }, "require": { "php": ">=8.1", "symfony/polyfill-ctype": "^1.8" }, "conflict": { "symfony/console": "<5.4" }, "require-dev": { "symfony/console": "^5.4|^6.0" }, "suggest": { "symfony/console": "For validating YAML files using the lint command" }, "bin": [ "Resources/bin/yaml-lint" ], "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\Yaml\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { "source": "https://github.com/symfony/yaml/tree/v6.2.5" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2023-01-10T18:53:53+00:00" }, { "name": "theseer/tokenizer", "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Arne Blankerts", "email": "arne@blankerts.de", "role": "Developer" } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", "source": "https://github.com/theseer/tokenizer/tree/1.2.1" }, "funding": [ { "url": "https://github.com/theseer", "type": "github" } ], "time": "2021-07-28T10:34:58+00:00" }, { "name": "webmozart/glob", "version": "4.6.0", "source": { "type": "git", "url": "https://github.com/webmozarts/glob.git", "reference": "3c17f7dec3d9d0e87b575026011f2e75a56ed655" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/webmozarts/glob/zipball/3c17f7dec3d9d0e87b575026011f2e75a56ed655", "reference": "3c17f7dec3d9d0e87b575026011f2e75a56ed655", "shasum": "" }, "require": { "php": "^7.3 || ^8.0.0" }, "require-dev": { "phpunit/phpunit": "^9.5", "symfony/filesystem": "^5.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.1-dev" } }, "autoload": { "psr-4": { "Webmozart\\Glob\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" } ], "description": "A PHP implementation of Ant's glob.", "support": { "issues": "https://github.com/webmozarts/glob/issues", "source": "https://github.com/webmozarts/glob/tree/4.6.0" }, "time": "2022-05-24T19:45:58+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { "php": "^7.1|^8.0" }, "platform-dev": [], "plugin-api-version": "2.3.0" } { "name": "wgirhad/geophp", "license": [ "GPL-2.0-or-later", "BSD-3-Clause" ], "type": "library", "description": "Open-source native PHP library for doing geometry operations. Can read and write a wide variety of formats: (E)WKT, (E)WKB, TWKB, GeoJSON, KML, GPX, GeoRSS. Works with all Simple-Feature geometries (Point, LineString, Polygon...) and can be used to get centroids, bounding-boxes, area, etc.", "keywords": ["geophp", "gis", "geometry", "converter", "linestring", "polygon", "wkt", "wkb", "kml", "gpx", "geojson", "twkb"], "homepage": "https://github.com/wgirhad/geoPHP", "authors": [ { "name": "Patrick Hayes", "role": "Original creator" }, { "name": "Péter Báthory", "role": "Developer" }, { "name": "Willian Girhad", "role": "Developer" } ], "autoload": { "psr-4": { "geoPHP\\": "src/" } }, "autoload-dev": { "psr-4": { "geoPHP\\Tests\\": "tests/" } }, "provide": { "geophp/geophp": "3.0.0" }, "require": { "php": "^7.1|^8.0" }, "require-dev": { "phpunit/phpunit": "^8.0|^9.0", "php-coveralls/php-coveralls": "2.*", "squizlabs/php_codesniffer": "3.*", "phpstan/phpstan": "^1.4.0", "phpbench/phpbench": "^1.0" }, "suggest": { "ext-geos": "GEOS allows more advanced operations" }, "scripts": { "tests": [ "@performance", "@bench", "@unit", "@test-input" ], "analyze": [ "@cs-warning", "@stan" ], "all": [ "@analyze", "echo \"Let's cool down a bit before testing benchmark ...\" && sleep 1", "@tests" ], "cs": "phpcs", "cs-warning": "phpcs -w", "cs-fix": "phpcbf", "stan": "phpstan analyze --no-progress", "unit": [ "@putenv XDEBUG_MODE=coverage", "phpunit" ], "test-input": "cd tests && php test.php && cd ..", "performance": "cd tests && php geometryPerformance.php && cd ..", "bench": "vendor/bin/phpbench run --report=aggregate --sleep 10000" } } <?php require_once dirname(__FILE__) . '../vendor/autoload.php'; use geoPHP\Geometry\Geometry; use geoPHP\geoPHP; function run_test(): void { header("Content-type: text"); // Your database test table should contain 3 columns: name (text), type (text), geom (geometry) // CREATE EXTENSION postgis; // CREATE TABLE geophp (id serial, name TEXT, type TEXT, geom Geometry, PRIMARY KEY (id)); $host = 'localhost'; $database = 'test'; $table = 'geophp'; $user = ''; $pass = ''; $connection = pg_connect("host=$host dbname=$database user=$user password=$pass"); if (!$connection) { die("Failed to connect to database. Test has been aborted.\n"); } if (@pg_query("SELECT '" . $table . "'::regclass") === false) { die("Table \"" . $table . "\" doesn't exists.\n"); } // Truncate pg_query($connection, "TRUNCATE TABLE $table"); // Working with PostGIS and EWKB // ---------------------------- foreach (scandir('./input') as $file) { $parts = explode('.', $file); if ($parts[0]) { $name = $file; $format = $parts[1]; $value = file_get_contents('./input/' . $file); echo '---- Testing ' . $file . "\n"; flush(); $geometry = geoPHP::load($value, $format); test_postgis($table, $name, $format, $geometry, $connection, 'wkb'); $geometry->setSRID(4326); test_postgis($table, $name, $format, $geometry, $connection, 'ewkb'); } } echo "Testing Done!\n"; } /** * @param string $table * @param string $name * @param string $type * @param Geometry $geom * @param resource|\PgSql\Connection $connection * @param string $format * @throws Exception */ function test_postgis(string $table, string $name, string $type, Geometry $geom, $connection, string $format): void { // Let's insert into the database using GeomFromWKB $insertString = pg_escape_bytea($geom->out($format)); pg_query( $connection, "INSERT INTO $table (name, type, geom) values ('$name', '$type', ST_GeomFromWKB('$insertString'))" ); // SELECT using asBinary PostGIS $result = pg_fetch_all( pg_query( $connection, "SELECT ST_AsBinary(geom) as geom FROM $table WHERE name='$name'" ) ) ?: []; foreach ($result as $item) { $wkb = pg_unescape_bytea($item['geom']); // Make sure to unescape the hex blob $geom = geoPHP::load($wkb, $format); // We now a full geoPHP Geometry object } // SELECT and INSERT directly, with no wrapping functions $result = pg_fetch_all(pg_query($connection, "SELECT geom as geom FROM $table WHERE name='$name'")) ?: []; foreach ($result as $item) { $geom = geoPHP::load($item['geom'], $format, true); // We now have a geoPHP Geometry // Let's re-insert directly into postGIS $insertString = $geom->out($format, true); pg_query($connection, "INSERT INTO $table (name, type, geom) values ('$name', '$type', '$insertString')"); } // SELECT and INSERT using as EWKT (ST_GeomFromEWKT and ST_AsEWKT) $result = pg_fetch_all( pg_query( $connection, "SELECT ST_AsEWKT(geom) as geom FROM $table WHERE name='$name'" ) ) ?: []; foreach ($result as $item) { $wkt = $item['geom']; // Make sure to unescape the hex blob $geom = geoPHP::load($item['geom'], 'ewkt'); // We now a full geoPHP Geometry object // Let's re-insert directly into postGIS $insertString = $geom->out('ewkt'); pg_query( $connection, "INSERT INTO $table (name, type, geom) values ('$name', '$type', ST_GeomFromEWKT('$insertString'))" ); } } run_test(); <?php require '../vendor/autoload.php'; /** * Some very simple performance test for Geometries. * Run before and after you modify geometries. * For example adding an array_merge() in a heavily used method can decrease performance dramatically. * * Please note, that this is not a real CI test, it will not fail on performance drops, just helps spotting them. * * Feel free to add more test methods. */ use geoPHP\Geometry\Point; use geoPHP\Geometry\LineString; use geoPHP\Geometry\Polygon; use geoPHP\Geometry\GeometryCollection; use geoPHP\geoPHP; /** Performance test will fail if running takes longer than MAX_RUN_TIME_SEC. (Yes, it's a bit of a dirty method.) */ const MAX_RUN_TIME_SEC = 10; const PRINTF_TEMPLATE_HEADER = "%-70s %-11s %-10s %-9s %s\n"; const PRINTF_TEMPLATE = "\r%-70s %.4f sec %7.3f MB %6.2f MB %s\n"; printf(PRINTF_TEMPLATE_HEADER, 'Test name', 'Time diff', 'Mem diff', 'Mem peak', 'Result'); function testStart(string $message): void { $GLOBALS['testRunTime'] = microtime(true); $GLOBALS['testStartMem'] = memory_get_usage(); $GLOBALS['testName'] = $message ; echo "\e[37m" . $message . " …\e[39m"; } /** * @param mixed|null $result * @param bool $ready */ function testEnd($result = '', bool $ready = false): void { if ($ready) { echo "\n"; printf( PRINTF_TEMPLATE, "TOTAL:", microtime(true) - $GLOBALS['startTime'], (memory_get_usage() - $GLOBALS['startMem']) / 1024 / 1024, memory_get_peak_usage() / 1024 / 1024, $result ); } else { printf( PRINTF_TEMPLATE, substr($GLOBALS['testName'], 0, 70), microtime(true) - $GLOBALS['testRunTime'], (memory_get_usage() - $GLOBALS['testStartMem']) / 1024 / 1024, memory_get_peak_usage() / 1024 / 1024, $result ); } } function runPerformanceTests(): void { geoPHP::disableGeos(); $pointCount = 10000; testStart("Creating " . $pointCount . " EMPTY Point"); /** @var Point[] $points */ $points = []; for ($i = 0; $i < $pointCount; $i++) { $points[] = new Point(); } testEnd(); testStart("Creating " . $pointCount . " Point"); $points = []; for ($i = 0; $i < $pointCount; $i++) { $points[] = new Point($i, $i + 1); } testEnd(); testStart("Creating " . $pointCount . " Point using ::fromArray()"); $points = []; for ($i = 0; $i < $pointCount; $i++) { $points[] = Point::fromArray([$i, $i + 1]); } testEnd(); testStart("Creating " . $pointCount . " PointZ"); $points = []; for ($i = 0; $i < $pointCount; $i++) { $points[] = new Point($i, $i + 1, $i + 2); } testEnd(); testStart("Creating " . $pointCount . " PointZM"); $points = []; for ($i = 0; $i < $pointCount; $i++) { $points[] = new Point($i, $i + 1, $i + 2, $i + 3); } testEnd(); testStart("Test points Point::is3D()"); foreach ($points as $point) { $point->is3D(); } testEnd(); testStart("Adding points to LineString"); $lineString = new LineString($points); testEnd(); testStart("Test LineString::invertXY()"); $lineString->invertXY(); testEnd(); testStart("Test LineString::explode(true)"); $res = count($lineString->explode(true)); testEnd($res . ' segment'); testStart("Test LineString::explode()"); $res = count($lineString->explode()); testEnd($res . ' segment'); testStart("Test LineString::length()"); $res = $lineString->length(); testEnd($res); testStart("Test LineString::greatCircleLength()"); $res = $lineString->greatCircleLength(); testEnd($res); testStart("Test LineString::haversineLength()"); $res = $lineString->haversineLength(); testEnd($res); testStart("Test LineString::vincentyLength()"); $res = $lineString->vincentyLength(); testEnd($res); $shorterLine = new LineString(array_slice($points, 0, min($pointCount, 300))); testStart("Test LineString::isSimple() (300 points long line)"); $res = $shorterLine->isSimple(); testEnd($res ? 'simple' : 'not simple'); $ringPoints = array_slice($points, 0, min($pointCount, 500 - 1)); $ringPoints[] = $ringPoints[0]; $shortRing = new LineString($ringPoints); $rings = unserialize(serialize(array_fill(0, 50, $shortRing))); testStart("Creating Polygon (50 ring, each has 500 point)"); $polygon = new Polygon($rings); testEnd(); $components = unserialize( serialize( array_merge( $points, array_fill(0, 50, $lineString), array_fill(0, 50, $polygon) ) ) ); testStart("Creating GeometryCollection (10000 point + 50 LineString + 50 polygon)"); $collection = new GeometryCollection($components); testEnd(); testStart("GeometryCollection::getPoints()"); $res = $collection->getPoints(); testEnd(count($res)); } $startTime = microtime(true); $startMem = memory_get_usage() / 1024 / 1024; runPerformanceTests(); testEnd(null, true); if (microtime(true) - $startTime > MAX_RUN_TIME_SEC) { echo "\e[31mTOO SLOW!\e[39m\n" . PHP_EOL; exit(1); } else { echo "\e[32mOK\e[39m\n" . PHP_EOL; exit(0); } <?php require '../vendor/autoload.php'; use geoPHP\Geometry\Geometry; use geoPHP\geoPHP; function runTest(): void { set_time_limit(0); set_error_handler("FailOnError"); header("Content-type: text"); if (geoPHP::isGeosInstalled()) { echo "GEOS is installed.\n"; } else { echo "GEOS is not installed.\n"; } $isVerbose = getenv("VERBOSE") == 1 || getopt('v'); $start = microtime(true); foreach (scandir('./input') as $file) { $parts = explode('.', $file); if ($parts[0]) { $startFile = microtime(true); $format = $parts[1]; $value = file_get_contents('./input/' . $file); echo '-- Testing ' . $file . "\n"; $geometry = geoPHP::load($value, $format); echo $isVerbose ? "---- Methods\n" : ''; testGeometryMethods($geometry); echo $isVerbose ? "---- Adapters\n" : ''; testAdapters($geometry, $format, $value); echo $isVerbose ? "---- Compare methods normal ←→ Geos\n" : ''; testGeosMethods($geometry); echo $isVerbose ? "---- Detection\n" : ''; testDetection($value, $format); echo ' ' . sprintf('%.3f', microtime(true) - $startFile) . " s\n"; } } echo "\nSuccessfully completed under " . sprintf('%.3f', microtime(true) - $start) . " seconds, using maximum " . sprintf('%.3f', memory_get_peak_usage() / 1024 / 1024) . " MB\n"; echo "\e[32mPASS\e[39m\n"; } /** * @param Geometry $geometry */ function testGeometryMethods(Geometry $geometry): void { // Test common functions $geometry->area(); $geometry->boundary(); $geometry->envelope(); $geometry->getBBox(); $geometry->centroid(); $geometry->length(); $geometry->greatCircleLength(); $geometry->haversineLength(); $geometry->x(); $geometry->y(); $geometry->numGeometries(); $geometry->geometryN(1); $geometry->startPoint(); $geometry->endPoint(); $geometry->isRing(); $geometry->isClosed(); $geometry->numPoints(); $geometry->pointN(1); $geometry->exteriorRing(); $geometry->numInteriorRings(); $geometry->interiorRingN(1); $geometry->coordinateDimension(); $geometry->geometryType(); $geometry->getSRID(); $geometry->setSRID(4326); $geometry->is3D(); $geometry->isMeasured(); $geometry->isEmpty(); $geometry->coordinateDimension(); $geometry->isSimple(); $geometry->equals($geometry); $geometry->asText(); $geometry->asBinary(); // Aliases $geometry->getCentroid(); $geometry->getArea(); $geometry->getX(); $geometry->getY(); $geometry->geos(); $geometry->SRID(); // GEOS only functions try { $geometry->flushGeosCache(); $geometry->getGeos(); $geometry->contains($geometry); $geometry->overlaps($geometry); $geometry->pointOnSurface(); $geometry->equalsExact($geometry); $geometry->relate($geometry); $geometry->checkValidity(); $geometry->buffer(10); $geometry->intersection($geometry); $geometry->convexHull(); $geometry->difference($geometry); $geometry->symDifference($geometry); $geometry->union($geometry); $geometry->simplify(0); $geometry->simplify(10); $geometry->simplify(100); $geometry->disjoint($geometry); $geometry->touches($geometry); $geometry->intersects($geometry); $geometry->crosses($geometry); $geometry->within($geometry); $geometry->covers($geometry); $geometry->coveredBy($geometry); $geometry->hausdorffDistance($geometry); // distance() is supported by geoPHP but too slow to test with each input $geometry->distance($geometry); } catch (\Exception $e) { if (getenv("VERBOSE") == 1 || getopt('v')) { echo "\e[33m\t" . $e->getMessage() . "\e[39m\n"; } } } /** * @param Geometry $geometry * @param string $format * @param string $input */ function testAdapters(Geometry $geometry, string $format, string $input): void { // Test adapter output and input. Do a round-trip and re-test foreach (geoPHP::getAdapterMap() as $adapterKey => $adapterClass) { if ($adapterKey == 'google_geocode') { //Don't test google geocoder regularily. Uncomment to test continue; } if (getenv("VERBOSE") == 1 || getopt('v')) { echo "\t {$adapterKey}\n"; } $output = $geometry->out($adapterKey); if ($output) { $adapterName = 'geoPHP\\Adapter\\' . $adapterClass; /** @var \geoPHP\Adapter\GeoAdapter */ $adapterLoader = new $adapterName(); try { $testGeom1 = $adapterLoader->read($output); $testGeom2 = $adapterLoader->read($testGeom1->out($adapterKey)); } catch (Exception $e) { echo "\e[31m\tException when reading output of " . $adapterClass . " adapter:\n"; echo $e->getMessage() . "\n Input: \n" . $input . "\n Output: \n" . $output . "\n"; echo "\e[39m\n"; exit(1); } if ($testGeom1->out('wkt') != $testGeom2->out('wkt')) { echo "\e[33m\tMismatched adapter output in " . $adapterClass . "\e[39m\n"; } } else { echo "\e[33m\tEmpty output on " . $adapterKey . "\e[39m\n"; } } // Test to make sure adapter work the same wether GEOS is ON or OFF // Cannot test methods if GEOS is not intstalled if (!geoPHP::isGeosInstalled()) { return; } if (getenv("VERBOSE") == 1 || getopt('v')) { echo "Testing with GEOS\n"; } foreach (geoPHP::getAdapterMap() as $adapterKey => $adapterClass) { if ($adapterKey == 'google_geocode') { //Don't test google geocoder regularily. Uncomment to test continue; } if (getenv("VERBOSE") == 1 || getopt('v')) { echo ' ' . $adapterClass . "\n"; } // Turn GEOS on geoPHP::enableGeos(); try { $output = $geometry->out($adapterKey); if ($output) { $adapterName = 'geoPHP\\Adapter\\' . $adapterClass; /** @var \geoPHP\Adapter\GeoAdapter */ $adapterLoader = new $adapterName(); $testGeomGeos = $adapterLoader->read($output); // Turn GEOS off geoPHP::disableGeos(); $testGeomNorm = $adapterLoader->read($output); // Check to make sure a both are the same with geos and without if ($testGeomGeos->out('wkt') != $testGeomNorm->out('wkt')) { $f = fopen('testGeomgeos.wkt', 'w+'); fwrite($f, $testGeomGeos->out('wkt')); fclose($f); $f = fopen('testGeomnorm.wkt', 'w+'); fwrite($f, $testGeomNorm->out('wkt')); fclose($f); $f = fopen('testGeomdump.geometry', 'w+'); fwrite($f, print_r($testGeomNorm, true)); fclose($f); echo "\e[31m\tMismatched adapter output between GEOS and NORM in {$adapterClass}. "; echo "Output written to files.\e[39m\n"; exit(1); } // Turn GEOS back on geoPHP::enableGeos(); } } catch (\geoPHP\Exception\UnsupportedMethodException $e) { if (getenv("VERBOSE") == 1 || getopt('v')) { echo "\e[33m\t" . $e->getMessage() . "\e[39m\n"; } } } } function testGeosMethods(Geometry $geometry): void { // Cannot test methods if GEOS is not intstalled if (!geoPHP::isGeosInstalled()) { return; } $methods = [ 'boundary', 'envelope', 'getBoundingBox', 'x', 'y', 'z', 'm', 'startPoint', 'endPoint', 'isRing', 'isClosed', 'numPoints', 'centroid', 'length', 'isEmpty', 'isSimple' ]; foreach ($methods as $method) { echo getenv("VERBOSE") == 1 ? ("\t" . $method . "\n") : ''; try { // Turn GEOS on geoPHP::enableGeos(); /** @var \geoPHP\Geometry\Geometry */ $geosResult = $geometry->$method(); // Turn GEOS off geoPHP::disableGeos(); /** @var \geoPHP\Geometry\Geometry */ $normResult = $geometry->$method(); // Turn GEOS back On geoPHP::enableGeos(); $geosType = gettype($geosResult); $normType = gettype($normResult); if ($geosType != $normType) { echo "\e[33mType mismatch on " . $method . "\e[39m\n"; continue; } // Now check base on type if ($geosType == 'object' && !$normResult->isEmpty()) { $hausDist = $geosResult->hausdorffDistance(geoPHP::load($normResult->out('wkt'), 'wkt')); // Get the length of the diagonal of the bbox - this is used to scale the haustorff distance // Using Pythagorean theorem $bBox = $geosResult->getBBox(); $scale = sqrt(pow($bBox['maxy'] - $bBox['miny'], 2) + pow($bBox['maxx'] - $bBox['minx'], 2)); // The difference in the output of GEOS and native-PHP methods // should be less than 0.5 scaled haustorff units if ($scale !== 0.0 && $hausDist / $scale > 0.5) { echo "\e[31mOutput mismatch on " . $method . "\e[39m\n"; echo 'GEOS : ' . $geosResult->out('wkt') . "\n"; echo 'NORM : ' . $normResult->out('wkt') . "\n"; exit(1); } } if (($geosType == 'boolean' || $geosType == 'string') && $geosResult !== $normResult) { echo "\e[31mOutput mismatch on " . $method . "\e[39m\n"; echo 'GEOS : ' . (string) $geosResult . "\n"; echo 'NORM : ' . (string) $normResult . "\n"; exit(1); } } catch (\geoPHP\Exception\UnsupportedMethodException $e) { if (getenv("VERBOSE") == 1 || getopt('v')) { echo "\e[33m\t" . $e->getMessage() . "\e[39m\n"; } } //@@TODO: Run tests for output of types arrays and float //@@TODO: centroid function is non-compliant for collections and strings } } function testDetection(string $value, string $format): void { $detected = geoPHP::detectFormat($value); if ($detected != $format) { if ($detected) { echo 'detected as ' . $detected . "\n"; } else { echo "format not detected\n"; } } // Make sure it loads using auto-detect geoPHP::load($value); } function FailOnError(int $errorLevel, string $errorMessage, string $errorFile, int $errorLine): ?bool { echo "$errorLevel: $errorMessage in $errorFile on line $errorLine\n"; echo "\e[31mFAIL\e[39m\n"; exit(1); } runTest(); <?php namespace geoPHP\Tests\Unit\Geometry; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\MultiGeometry; use geoPHP\Geometry\Point; use PHPUnit\Framework\TestCase; /** * Unit tests of abstract geometry MultiGeometry * * @group geometry * @coversDefaultClass geoPHP\Geometry\MultiGeometry * * @uses geoPHP\Geometry\Point */ class MultiGeometryTest extends TestCase { /** * @covers ::__construct */ public function testConstructor(): void { $geom1 = $this->getMockForAbstractClass(Geometry::class, []); $this->assertInstanceOf( MultiGeometry::class, $this->getMockForAbstractClass(MultiGeometry::class, [[$geom1]]) ); $this->assertInstanceOf( MultiGeometry::class, $this->getMockForAbstractClass(MultiGeometry::class, [[$geom1], Geometry::class]) ); $this->assertInstanceOf( MultiGeometry::class, $this->getMockForAbstractClass(MultiGeometry::class, [[$geom1], Geometry::class, true]) ); } /** * @return array<mixed> */ public function providerIsSimple(): array { return [ 'empty' => [[], true], 'two point' => [[new Point(), new Point()], true], 'two curves, second is empty' => [[[[0, 0], [1, 1]], []]], ]; } public function testIsSimple(): void { $simpleGeom = $this->getMockForAbstractClass(Geometry::class); $simpleGeom->expects($this->any()) ->method('isSimple') ->willReturn(true); $nonSimpleGeom = $this->getMockForAbstractClass(Geometry::class); $nonSimpleGeom->expects($this->any()) ->method('isSimple') ->willReturn(false); // Empty components => simple $stub = $this->getMockForAbstractClass(MultiGeometry::class, [[]]); $this->assertTrue($stub->isSimple()); // Simple components => simple $stub = $this->getMockForAbstractClass(MultiGeometry::class, [[$simpleGeom, $simpleGeom]]); $this->assertTrue($stub->isSimple()); // One simple and one non simple component => not simple $stub = $this->getMockForAbstractClass(MultiGeometry::class, [[$simpleGeom, $nonSimpleGeom]]); $this->assertFalse($stub->isSimple()); // Two non simple component => not simple $stub = $this->getMockForAbstractClass(MultiGeometry::class, [[$nonSimpleGeom, $nonSimpleGeom]]); $this->assertFalse($stub->isSimple()); } } <?php namespace geoPHP\Tests\Unit\Geometry; use geoPHP\Exception\InvalidGeometryException; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\LineString; use geoPHP\Geometry\Point; use geoPHP\Geometry\Polygon; use PHPUnit\Framework\TestCase; /** * Unit tests of Polygon geometry * * @group geometry * @coversDefaultClass geoPHP\Geometry\Polygon * * @uses geoPHP\Geometry\Point * @uses geoPHP\Geometry\LineString */ class PolygonTest extends TestCase { /** * @param array<mixed> $coordinateArray * @return LineString[] */ private function createComponents(array $coordinateArray): array { $lines = []; foreach ($coordinateArray as $point) { $lines[] = LineString::fromArray($point); } return $lines; } /** * @return array<string, mixed> */ public function providerConstructorValidComponents(): array { return [ 'empty' => [[]], 'of 4 points' => [[[[0, 0], [0, 1], [1, 1], [0, 0]]]], 'Polygon Z' => [[[[0, 0, 0], [0, 1, 1], [1, 1, 2], [0, 0, 3]]]], 'Polygon M' => [[[[0, 0, null, 0], [0, 1, null, 1], [1, 1, null, 2], [0, 0, null, 3]]]], 'Polygon ZM' => [[[[0, 0, 0, 0], [0, 1, 1, 1], [1, 1, 2, 2], [0, 0, 3, 3]]]], 'Polygon with two rings' => [[[[0, 0], [0, 10], [10, 10], [10, 0], [0, 0]], [[2, 2], [2, 4], [3, 4], [2, 2]]]], ]; } /** * @dataProvider providerConstructorValidComponents * @covers ::__construct * * @param array<mixed> $points */ public function testConstructor(array $points): void { $polygon = new Polygon($this->createComponents($points)); $this->assertNotNull($polygon); $this->assertInstanceOf(Polygon::class, $polygon); } /** * @covers ::__construct */ public function testConstructorNonArrayComponentTypeError(): void { $this->expectException(\TypeError::class); $this->expectErrorMessageMatches('/Argument #?1 .+ type array, string given/'); // @phpstan-ignore-next-line new Polygon('foo'); } /** * @covers ::__construct */ public function testConstructorEmptyComponent(): void { $this->expectException(InvalidGeometryException::class); $this->expectErrorMessageMatches('/Cannot create a collection of empty LineStrings/'); new Polygon([new LineString()]); } /** * @return array<string, array<LineString>> */ public function providerConstructorFewPoints(): array { return [ 'two points' => [LineString::fromArray([[1, 2], [2, 3]])], 'three points' => [LineString::fromArray([[1, 2], [2, 3], [4, 5]])], ]; } /** * @dataProvider providerConstructorFewPoints * @covers ::__construct */ public function testConstructorFewPointThrowsException(LineString $component): void { $this->expectException(InvalidGeometryException::class); $this->expectExceptionMessageMatches( '/Cannot create Polygon: Invalid number of points in LinearRing. Found \d+, expected more than 3/' ); new Polygon([$component]); } /** * @covers ::__construct */ public function testConstructorWrongComponentTypeThrowsException(): void { $this->expectException(InvalidGeometryException::class); $this->expectExceptionMessageMatches('/Cannot construct .+Polygon\. Expected .+LineString components, got.+/'); // @phpstan-ignore-next-line new Polygon([new Point()]); } /** * @return array<mixed> */ public function providerValidComponents(): array { $ring1Points = [[0, 0], [0, 10], [10, 10], [10, 0], [0, 0]]; $ring2Points = [[1, 1], [1, 9], [5, 9], [1, 1]]; $ring3Points = [[2, 2], [2, 8], [5, 8], [2, 2]]; $ring1 = new LineString( [new Point(0, 0), new Point(0, 10), new Point(10, 10), new Point(10, 0), new Point(0, 0)] ); $ring2 = new LineString([new Point(1, 1), new Point(1, 9), new Point(5, 9), new Point(1, 1)]); $ring3 = new LineString([new Point(2, 2), new Point(2, 8), new Point(5, 8), new Point(2, 2)]); return [ 'empty' => [ [], new Polygon(), [ 'exteriorRing' => new LineString(), 'interiorRings' => [], 'boundaryType' => Geometry::LINE_STRING, ] ], 'one ring' => [ [$ring1Points], new Polygon([$ring1]), [ 'exteriorRing' => $ring1, 'interiorRings' => [], 'boundaryType' => Geometry::LINE_STRING, ] ], 'two ring' => [ [$ring1Points, $ring2Points], new Polygon([$ring1, $ring2]), [ 'exteriorRing' => $ring1, 'interiorRings' => [1 => $ring2], 'boundaryType' => Geometry::MULTI_LINE_STRING, ] ], 'three ring' => [ [$ring1Points, $ring2Points, $ring3Points], new Polygon([$ring1, $ring2, $ring3]), [ 'exteriorRing' => $ring1, 'interiorRings' => [1 => $ring2, 2 => $ring3], 'boundaryType' => Geometry::MULTI_LINE_STRING, ] ], ]; } /** * @dataProvider providerValidComponents * @covers ::fromArray * * @param array<?array<array<int|float>>> $points * @param Polygon $expectedGeometry */ public function testFromArray(array $points, Polygon $expectedGeometry): void { $fromArray = Polygon::fromArray($points); $this->assertEquals($expectedGeometry, $fromArray); } /** * @covers ::geometryType */ public function testGeometryType(): void { $polygon = new Polygon(); $this->assertEquals(Polygon::POLYGON, $polygon->geometryType()); $this->assertInstanceOf(Polygon::class, $polygon); $this->assertInstanceOf(\geoPHP\Geometry\Surface::class, $polygon); $this->assertInstanceOf(\geoPHP\Geometry\Collection::class, $polygon); $this->assertInstanceOf(\geoPHP\Geometry\Geometry::class, $polygon); } /** * @covers ::dimension */ public function testDimension(): void { $polygon = new Polygon(); $this->assertSame(2, $polygon->dimension()); } /** * @dataProvider providerValidComponents * @covers ::exteriorRing * * @param array<array<int>> $points * @param Polygon $geometry * @param array<mixed> $results */ public function testExteriorRing(array $points, Polygon $geometry, array $results): void { $this->assertEquals($results['exteriorRing'], $geometry->exteriorRing()); } /** * @dataProvider providerValidComponents * @covers ::numInteriorRings * * @param array<array<int>> $points * @param Polygon $geometry * @param array<mixed> $results */ public function testNumInteriorRings(array $points, Polygon $geometry, array $results): void { $this->assertSame(count($results['interiorRings']), $geometry->numInteriorRings()); } /** * @dataProvider providerValidComponents * @covers ::interiorRingN * * @param array<array<int>> $points * @param Polygon $geometry * @param array<mixed> $results */ public function testInteriorRingN(array $points, Polygon $geometry, array $results): void { if (!count($results['interiorRings'])) { $this->expectNotToPerformAssertions(); } foreach ($results['interiorRings'] as $num => $ring) { $this->assertEquals($ring, $geometry->interiorRingN($num)); } } /** * @dataProvider providerValidComponents * @covers ::boundary * * @param array<array<int>> $points * @param Polygon $geometry * @param array<mixed> $results */ public function testBoundary(array $points, Polygon $geometry, array $results): void { $boundary = $geometry->boundary(); $this->assertInstanceOf(Geometry::class, $boundary); $this->assertEquals($results['boundaryType'], $boundary->geometryType()); } } <?php namespace geoPHP\Tests\Unit\Geometry; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\Polygon; use geoPHP\Geometry\Surface; use PHPUnit\Framework\TestCase; /** * Unit tests of abstract Surface geometry * * @group geometry * @coversDefaultClass geoPHP\Geometry\Surface * * @uses geoPHP\Geometry\Point * @uses geoPHP\Geometry\LineString */ class SurfaceTest extends TestCase { /** * @covers ::__construct */ public function testConstructor(): void { $stub = $this->getMockForAbstractClass(Surface::class, [[]]); $this->assertNotNull($stub); } /** * @covers ::geometryType */ public function testGeometryType(): void { $stub = $this->getMockForAbstractClass(Surface::class, [[]]); $this->assertEquals(Polygon::SURFACE, $stub->geometryType()); $this->assertInstanceOf(Surface::class, $stub); $this->assertInstanceOf(\geoPHP\Geometry\Collection::class, $stub); $this->assertInstanceOf(Geometry::class, $stub); } /** * @covers ::dimension */ public function testDimension(): void { $stub = $this->getMockForAbstractClass(Surface::class, [[]]); $this->assertSame(2, $stub->dimension()); } /** * @covers ::isEmpty */ public function testIsEmpty(): void { $stub = $this->getMockForAbstractClass(Surface::class, [[]]); $this->assertTrue($stub->isEmpty()); $geometryStub = $this->getMockForAbstractClass(Geometry::class, []); $stub = $this->getMockForAbstractClass(Surface::class, [[$geometryStub]]); $this->assertFalse($stub->isEmpty()); } /** * @covers ::startPoint * @covers ::endPoint * @covers ::pointN * @covers ::isClosed * @covers ::isRing * @covers ::length * @covers ::length3D * @covers ::haversineLength * @covers ::vincentyLength * @covers ::greatCircleLength * @covers ::minimumZ * @covers ::maximumZ * @covers ::minimumM * @covers ::maximumM * @covers ::elevationGain * @covers ::elevationLoss * @covers ::zDifference */ public function testTrivialMethods(): void { $stub = $this->getMockForAbstractClass(Surface::class, [[]]); $this->assertSame(null, $stub->startPoint()); $this->assertSame(null, $stub->endPoint()); $this->assertSame(null, $stub->pointN(1)); $this->assertSame(null, $stub->isClosed()); $this->assertSame(null, $stub->isRing()); $this->assertSame(0.0, $stub->length()); $this->assertSame(0.0, $stub->length3D()); $this->assertSame(0.0, $stub->haversineLength()); $this->assertSame(0.0, $stub->vincentyLength()); $this->assertSame(0.0, $stub->greatCircleLength()); $this->assertSame(null, $stub->minimumZ()); $this->assertSame(null, $stub->maximumZ()); $this->assertSame(null, $stub->minimumM()); $this->assertSame(null, $stub->maximumM()); $this->assertSame(null, $stub->elevationGain()); $this->assertSame(null, $stub->elevationLoss()); $this->assertSame(null, $stub->zDifference()); } } <?php namespace geoPHP\Tests\Unit\Geometry; use geoPHP\Exception\InvalidGeometryException; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\GeometryCollection; use geoPHP\Geometry\LineString; use geoPHP\Geometry\MultiPoint; use geoPHP\Geometry\Point; use PHPUnit\Framework\TestCase; /** * Unit tests of Point geometry * * @coversDefaultClass geoPHP\Geometry\Point * @group geometry */ class PointTest extends TestCase { public const DELTA = 1e-8; /** * @return array<string, array<int|float>> */ public function providerValidCoordinatesXY(): array { return [ 'null coordinates' => [0, 0], 'positive integer' => [10, 20], 'negative integer' => [-10, -20], 'WGS84' => [47.1234056789, 19.9876054321], 'HD72/EOV' => [238084.12, 649977.59], ]; } /** * @dataProvider providerValidCoordinatesXY * @covers ::__construct * @covers ::x * @covers ::y * * @param int|float $x * @param int|float $y */ public function testValidCoordinatesXY($x, $y): void { $point = new Point($x, $y); $this->assertEquals($x, $point->x()); $this->assertEquals($y, $point->y()); $this->assertNull($point->z()); $this->assertNull($point->m()); $this->assertIsFloat($point->x()); $this->assertIsFloat($point->y()); } /** * @return array<string, array<int|float>> */ public function providerValidCoordinatesXYZorXYM(): array { return [ 'null coordinates' => [0, 0, 0], 'positive integer' => [10, 20, 30], 'negative integer' => [-10, -20, -30], 'WGS84' => [47.1234056789, 19.9876054321, 100.1], 'HD72/EOV' => [238084.12, 649977.59, 56.38], ]; } /** * @dataProvider providerValidCoordinatesXYZorXYM * @covers ::__construct * @covers ::x * @covers ::y * @covers ::z * * @param int|float $x * @param int|float $y * @param int|float $z */ public function testValidCoordinatesXYZ($x, $y, $z): void { $point = new Point($x, $y, $z); $this->assertEquals($x, $point->x()); $this->assertEquals($y, $point->y()); $this->assertEquals($z, $point->z()); $this->assertNull($point->m()); $this->assertIsFloat($point->x()); $this->assertIsFloat($point->y()); $this->assertIsFloat($point->z()); } /** * @dataProvider providerValidCoordinatesXYZorXYM * @covers ::__construct * @covers ::x * @covers ::y * @covers ::m * * @param int|float $x * @param int|float $y * @param int|float $m */ public function testValidCoordinatesXYM($x, $y, $m): void { $point = new Point($x, $y, null, $m); $this->assertEquals($x, $point->x()); $this->assertEquals($y, $point->y()); $this->assertEquals($m, $point->m()); $this->assertNull($point->z()); $this->assertIsFloat($point->x()); $this->assertIsFloat($point->y()); $this->assertIsFloat($point->m()); } /** * @return array<string, array<int|float>> */ public function providerValidCoordinatesXYZM(): array { return [ 'null coordinates' => [0, 0, 0, 0], 'positive integer' => [10, 20, 30, 40], 'negative integer' => [-10, -20, -30, -40], 'WGS84' => [47.1234056789, 19.9876054321, 100.1, 0.00001], 'HD72/EOV' => [238084.12, 649977.59, 56.38, -0.00001], ]; } /** * @dataProvider providerValidCoordinatesXYZM * @covers ::__construct * @covers ::x * @covers ::y * @covers ::z * @covers ::m * * @param int|float $x * @param int|float $y * @param int|float $z * @param int|float $m */ public function testValidCoordinatesXYZM($x, $y, $z, $m): void { $point = new Point($x, $y, $z, $m); $this->assertEquals($x, $point->x()); $this->assertEquals($y, $point->y()); $this->assertEquals($z, $point->z()); $this->assertEquals($m, $point->m()); $this->assertIsFloat($point->x()); $this->assertIsFloat($point->y()); $this->assertIsFloat($point->z()); $this->assertIsFloat($point->m()); } /** * @covers ::__construct * @covers ::x * @covers ::y * @covers ::z * @covers ::m */ public function testConstructorWithoutParameters(): void { $point = new Point(); $this->assertTrue($point->isEmpty()); $this->assertNull($point->x()); $this->assertNull($point->y()); $this->assertNull($point->z()); $this->assertNull($point->m()); } /** * @return array<string, array<?int>> */ public function providerIsEmpty(): array { return [ 'no coordinates' => [], 'x and y is null' => [null, null, 30], 'x, y, z is null' => [null, null, null, 40], 'x, y, z, m is null' => [null, null, null, null], ]; } /** * @dataProvider providerIsEmpty * @covers ::isEmpty * @covers ::__construct * * @param int|float|null $x * @param int|float|null $y * @param int|float|null $z * @param int|float|null $m */ public function testIsEmpty($x = null, $y = null, $z = null, $m = null): void { $point = new Point($x, $y, $z, $m); $this->assertTrue($point->isEmpty()); $this->assertNull($point->x()); $this->assertNull($point->y()); $this->assertNull($point->z()); $this->assertNull($point->m()); } /** * @dataProvider providerValidCoordinatesXY * @dataProvider providerValidCoordinatesXYZorXYM * @dataProvider providerValidCoordinatesXYZM * @dataProvider providerIsEmpty * * @covers ::fromArray * * @param int|float|null $x * @param int|float|null $y * @param int|float|null $z * @param int|float|null $m */ public function testFromArray($x = null, $y = null, $z = null, $m = null): void { $positions = [$x, $y, $z, $m]; $point = Point::fromArray($positions); $this->assertEquals($x, $point->x()); $this->assertEquals($y, $point->y()); if ($x !== null) { $this->assertEquals($z, $point->z()); $this->assertEquals($m, $point->m()); } } /** * @covers ::numPoints */ public function testNumPoints(): void { $point = new Point(1, 2); $this->assertSame(1, $point->numPoints()); $pointEmpty = new Point(); $this->assertSame(0, $pointEmpty->numPoints()); } /** * @return array<string, array<mixed>> */ public function providerInvalidCoordinates(): array { return [ 'x is null' => [null, 20], 'y is null' => [10, null], 'string coordinates' => ['x', 'y'], 'boolean coordinates' => [true, false], 'z is non numeric' => [1, 2, 'z'], 'm is non numeric' => [1, 2, 3, 'm'], 'x is NaN' => [NAN, 1], 'y is NaN' => [1, NAN], 'x is infinite' => [INF, 1], 'y is infinite' => [1, INF], 'z is NaN' => [1, 2, NAN], 'z is infinite' => [1, 2, INF], 'm is NaN' => [1, 2, 3, NAN], 'm is infinite' => [1, 2, 3, INF], ]; } /** * @dataProvider providerInvalidCoordinates * @covers ::__construct * * @param mixed $x * @param mixed $y * @param mixed $z * @param mixed $m */ public function testConstructorWithInvalidCoordinates($x, $y, $z = null, $m = null): void { $this->expectException(InvalidGeometryException::class); new Point($x, $y, $z, $m); } /** * @covers ::geometryType */ public function testGeometryType(): void { $point = new Point(); $this->assertEquals(Geometry::POINT, $point->geometryType()); $this->assertInstanceOf(Point::class, $point); $this->assertInstanceOf(Geometry::class, $point); } /** * @return array<string, array<bool|int|null>> */ public function providerIs3D(): array { return [ 'empty point' => [false], '2 coordinates' => [false, 1, 2], '3 coordinates' => [true, 1, 2, 3], '4 coordinates' => [true, 1, 2, 3, 4], 'empty point with z' => [false, null, null, 3, 4], 'z is null' => [false, 1, 2, null, 4], ]; } /** * @dataProvider providerIs3D * @covers ::is3D * * @param bool $result * @param int|float|null $x * @param int|float|null $y * @param int|float|null $z * @param int|float|null $m */ public function testIs3D(bool $result, $x = null, $y = null, $z = null, $m = null): void { $this->assertSame($result, (new Point($x, $y, $z, $m))->is3D()); } /** * @return array<string, array<bool|int|null>> */ public function providerIsMeasured(): array { return [ 'empty point' => [false], '2 coordinates is false' => [false, 1, 2], '3 coordinates is false' => [false, 1, 2, 3], '4 coordinates' => [true, 1, 2, 3, 4], 'empty point with z and m' => [false, null, null, 3, 4], 'empty point with m' => [false, null, null, null, 4], 'm is null' => [false, 1, 2, 3, null], 'z is null' => [true, 1, 2, null, 4], ]; } /** * @dataProvider providerIsMeasured * @covers ::isMeasured * * @param bool $result * @param int|float|null $x * @param int|float|null $y * @param int|float|null $z * @param int|float|null $m */ public function testIsMeasured(bool $result, $x = null, $y = null, $z = null, $m = null): void { $this->assertSame($result, (new Point($x, $y, $z, $m))->isMeasured()); } /** * @covers ::getComponents */ public function testGetComponents(): void { $point = new Point(1, 2); $components = $point->getComponents(); $this->assertIsArray($components); $this->assertCount(1, $components); $this->assertSame($point, $components[0]); } /** * @dataProvider providerValidCoordinatesXYZM * @covers ::invertXY * * @param int|float|null $x * @param int|float|null $y * @param int|float|null $z * @param int|float|null $m */ public function testInvertXY($x, $y, $z, $m): void { $point = new Point($x, $y, $z, $m); $originalPoint = clone $point; $point->invertXY(); $this->assertEquals($x, $point->y()); $this->assertEquals($y, $point->x()); $this->assertEquals($z, $point->z()); $this->assertEquals($m, $point->m()); $point->invertXY(); $this->assertEquals($point, $originalPoint); } /** * @covers ::centroid */ public function testCentroidIsThePointItself(): void { $point = new Point(1, 2, 3, 4); $this->assertSame($point, $point->centroid()); } /** * @covers ::getBBox */ public function testBBox(): void { $point = new Point(1, 2); $this->assertSame( $point->getBBox(), [ 'maxy' => 2.0, 'miny' => 2.0, 'maxx' => 1.0, 'minx' => 1.0, ] ); } /** * @covers ::getBBox */ public function testBBoxOfEmpty(): void { $point = new Point(); $this->assertSame( $point->getBBox(), [ 'maxy' => null, 'miny' => null, 'maxx' => null, 'minx' => null, ] ); } /** * @covers ::asArray * @testWith [[]] * [[1.0, 2.0]] * [[1.0, 2.0, 3.0]] * [[1.0, 2.0, null, 3.0]] * [[1.0, 2.0, 3.0, 4.0]] * * @param array<?float> $points */ public function testAsArray(array $points): void { $point = Point::fromArray($points); $pointAsArray = $point->asArray(); $this->assertSame($points, $pointAsArray); } /** * @covers ::boundary */ public function testBoundary(): void { $this->assertEquals((new Point(1, 2))->boundary(), new GeometryCollection()); } /** * @covers ::equals */ public function testEquals(): void { $this->assertTrue((new Point())->equals(new Point())); $point = new Point(1, 2, 3, 4); $this->assertTrue($point->equals(new Point(1, 2, 3, 4))); $this->assertTrue($point->equals(new Point(1.0000000001, 2.0000000001, 3, 4))); $this->assertTrue($point->equals(new Point(0.9999999999, 1.9999999999, 3, 4))); $this->assertFalse($point->equals(new Point(1.000000001, 2.000000001, 3, 4))); $this->assertFalse($point->equals(new Point(0.999999999, 1.999999999, 3, 4))); $this->assertFalse($point->equals(new GeometryCollection())); } /** * @covers ::flatten */ public function testFlatten(): void { $point = new Point(1, 2, 3, 4); $point->flatten(); $this->assertEquals(1, $point->x()); $this->assertEquals(2, $point->y()); $this->assertNull($point->z()); $this->assertNull($point->m()); $this->assertFalse($point->is3D()); $this->assertFalse($point->isMeasured()); } /** * @return array<string, array{Geometry, ?float}> */ public function providerDistance(): array { return [ 'empty Point' => [new Point(), null], 'Point x+10' => [new Point(10, 0), 10.0], 'Point y+10' => [new Point(0, 10), 10.0], 'Point x+10,y+10' => [new Point(10, 10), 14.142135623730951], 'LineString, point is the first vertex' => [LineString::fromArray([[0, 0], [10, 10]]), 0.0], 'LineString, point is the second vertex' => [LineString::fromArray([[-10, 10], [0, 0]]), 0.0], 'LineString, containing a vertex twice' => [LineString::fromArray([[0, 10], [0, 10]]), 10.0], 'LineString, point on line' => [LineString::fromArray([[-10, -10], [10, 10]]), 0.0], 'MultiPoint, closest distance is 0' => [MultiPoint::fromArray([[0, 0], [10, 20]]), 0.0], 'MultiPoint, closest distance is 10' => [MultiPoint::fromArray([[10, 20], [0, 10]]), 10.0], 'MultiPoint, one is empty' => [MultiPoint::fromArray([[10, 0], []]), 10.0], 'GeometryCollection, closest component is 10' => [new GeometryCollection([new Point(0, 10), new Point(20, 0)]), 10.0] // TODO: test other types ]; } /** * @dataProvider providerDistance * @covers ::distance */ public function testDistance(Geometry $otherGeometry, ?float $expectedDistance): void { $point = new Point(0, 0); $this->assertEqualsWithDelta($expectedDistance, $point->distance($otherGeometry), self::DELTA); } /** * @dataProvider providerDistance * @covers ::distance */ public function testDistanceEmpty(Geometry $otherGeometry): void { $point = new Point(); $this->assertNull($point->distance($otherGeometry)); } /** * @covers ::dimension * @covers ::getPoints * @covers ::isSimple */ public function testTrivialMethods(): void { $point = new Point(1, 2, 3, 4); $this->assertSame(0, $point->dimension()); $this->assertSame([$point], $point->getPoints()); $this->assertTrue($point->isSimple()); } /** * @covers ::minimumZ * @covers ::maximumZ * @covers ::minimumM * @covers ::maximumM */ public function testMinMaxMethods(): void { $point = new Point(1, 2, 3, 4); $this->assertEquals(3, $point->minimumZ()); $this->assertEquals(3, $point->maximumZ()); $this->assertEquals(4, $point->minimumM()); $this->assertEquals(4, $point->maximumM()); } /** * @return array{array<string>} */ public function providerMethodsNotValidForPointReturnsNull(): array { return [ ['zDifference'], ['elevationGain'], ['elevationLoss'], ['numGeometries'], ['geometryN'], ['startPoint'], ['endPoint'], ['isRing'], ['isClosed'], ['pointN'], ['exteriorRing'], ['numInteriorRings'], ['interiorRingN'], ['explode'] ]; } /** * @dataProvider providerMethodsNotValidForPointReturnsNull * @covers ::zDifference * @covers ::elevationGain * @covers ::elevationLoss * @covers ::numGeometries * @covers ::geometryN * @covers ::startPoint * @covers ::endPoint * @covers ::isRing * @covers ::isClosed * @covers ::pointN * @covers ::exteriorRing * @covers ::numInteriorRings * @covers ::interiorRingN * @covers ::explode */ public function testPlaceholderMethodsReturnsNull(string $methodName): void { $this->assertNull((new Point(1, 2, 3, 4))->$methodName()); } /** * @testWith ["area"] * ["length"] * ["length3D"] * ["greatCircleLength"] * ["haversineLength"] * ["vincentyLength"] * * @covers ::area * @covers ::length * @covers ::length3D * @covers ::greatCircleLength * @covers ::haversineLength * @covers ::vincentyLength */ public function testPlaceholderMethods(string $methodName): void { $this->assertSame(0.0, (new Point(1, 2, 3, 4))->$methodName()); } } <?php namespace geoPHP\Tests\Unit\Geometry; use geoPHP\Exception\InvalidGeometryException; use geoPHP\Geometry\LineString; use geoPHP\Geometry\Point; use geoPHP\Geometry\MultiPoint; use PHPUnit\Framework\TestCase; /** * Unit tests of MultiPoint geometry. * * @group geometry * @coversDefaultClass geoPHP\Geometry\MultiPoint * * @uses geoPHP\Geometry\Point * @uses geoPHP\Geometry\LineString * @uses geoPHP\Geometry\Collection */ class MultiPointTest extends TestCase { /** * @return array<string, array<array<?Point>>> */ public function providerValidComponents(): array { return [ 'no components' => [[]], 'empty Point comp' => [[new Point()]], 'xy' => [[new Point(1, 2)]], '2 xy' => [[new Point(1, 2), new Point(3, 4)]], '2 xyzm' => [[new Point(1, 2, 3, 4), new Point(5, 6, 7, 8)]], 'one is empty' => [[new Point(), new Point(1, 2)]], ]; } /** * @dataProvider providerValidComponents * @covers ::__construct * * @param array<?Point> $points */ public function testValidComponents(array $points): void { $multiPoint = new MultiPoint($points); $this->assertNotNull($multiPoint); $this->assertInstanceOf(MultiPoint::class, $multiPoint); } /** * @return array<string, array<mixed>> */ public function providerInvalidComponents(): array { return [ 'LineString component' => [[LineString::fromArray([[1,2],[3,4]])]], 'string component' => [["text"]], ]; } /** * @dataProvider providerInvalidComponents * @covers ::__construct * * @param array<mixed> $components */ public function testConstructorWithInvalidComponents($components): void { $this->expectException(InvalidGeometryException::class); new MultiPoint($components); } /** * @covers ::fromArray */ public function testFromArray(): void { $this->assertEquals( MultiPoint::fromArray([[1,2,3,4], [5,6,7,8]]), new MultiPoint([new Point(1, 2, 3, 4), new Point(5, 6, 7, 8)]) ); } /** * @covers ::__construct * @covers ::geometryType */ public function testGeometryType(): void { $multiPoint = new MultiPoint(); $this->assertEquals(\geoPHP\Geometry\Geometry::MULTI_POINT, $multiPoint->geometryType()); $this->assertInstanceOf('\geoPHP\Geometry\MultiPoint', $multiPoint); $this->assertInstanceOf('\geoPHP\Geometry\MultiGeometry', $multiPoint); $this->assertInstanceOf('\geoPHP\Geometry\Geometry', $multiPoint); } /** * @covers ::is3D */ public function testIs3D(): void { $this->assertFalse((new MultiPoint([new Point(1, 2)]))->is3D()); $this->assertTrue((new MultiPoint([new Point(1, 2, 3)]))->is3D()); $this->assertTrue((new MultiPoint([new Point(1, 2, 3, 4)]))->is3D()); } /** * @covers ::isMeasured */ public function testIsMeasured(): void { $this->assertFalse((new MultiPoint([new Point(1, 2)]))->isMeasured()); $this->assertFalse((new MultiPoint([new Point(1, 2, 3)]))->isMeasured()); $this->assertTrue((new MultiPoint([new Point(1, 2, 3, 4)]))->isMeasured()); } /** * @return array<mixed> */ public function providerCentroid(): array { return [ [[], []], [[[0, 0], [0, 10]], [0, 5]] ]; } /** * @dataProvider providerCentroid * @covers ::centroid * * @param array<mixed> $components * @param array<int> $expectedCentroid */ public function testCentroid(array $components, array $expectedCentroid): void { $multiPoint = MultiPoint::fromArray($components); $centroid = $multiPoint->centroid(); $this->assertEquals(Point::fromArray($expectedCentroid), $centroid); } /** * @return array{array{array<mixed>, bool}} */ public function providerIsSimple(): array { return [ [[], true], [[[0, 0], [0, 10]], true], [[[1, 1], [2, 2], [1, 3], [1, 2], [2, 1]], true], [[[0, 10], [0, 10]], false], ]; } /** * @dataProvider providerIsSimple * @covers ::isSimple * * @param array<mixed> $points * @param bool $result */ public function testIsSimple(array $points, bool $result): void { $multiPoint = MultiPoint::fromArray($points); $this->assertSame($result, $multiPoint->isSimple()); } /** * @return array<string, array<int|Point[]>> */ public function providerNumPoints(): array { return [ 'no components' => [0, []], 'empty Point comp' => [0, [new Point()]], 'xy' => [1, [new Point(1, 2)]], '2 xy' => [2, [new Point(1, 2), new Point(3, 4)]], 'one is empty' => [1, [new Point(), new Point(1, 2)]], ]; } /** * @dataProvider providerNumPoints * @covers ::numPoints * * @param Point[]|array{} $points */ public function testNumPoints(int $expectedNum, array $points): void { $multiPoint = new MultiPoint($points); $this->assertEquals($expectedNum, $multiPoint->numPoints()); } /** * @dataProvider providerValidComponents * @covers ::dimension * @covers ::boundary * @covers ::explode * * @param array<mixed> $points */ public function testTrivialAndNotValidMethods(array $points): void { $point = new MultiPoint($points); $this->assertSame(0, $point->dimension()); $this->assertEquals(new \geoPHP\Geometry\GeometryCollection(), $point->boundary()); $this->assertNull($point->explode()); $this->assertTrue($point->isSimple()); } } <?php namespace geoPHP\Tests\Unit\Geometry; use geoPHP\Exception\InvalidGeometryException; use geoPHP\Geometry\Curve; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\MultiCurve; use geoPHP\Geometry\MultiPoint; use geoPHP\Geometry\Point; use PHPUnit\Framework\TestCase; /** * Unit tests of abstract geometry MultiCurve * * @group geometry * @coversDefaultClass geoPHP\Geometry\MultiCurve * * @uses geoPHP\Geometry\Point */ class MultiCurveTest extends TestCase { /** * @param array<array<array<int|float>>> $coordinateArray * @return Curve[] */ private function createCurves(array $coordinateArray): array { $curves = []; foreach ($coordinateArray as $curvePoints) { $points = []; foreach ($curvePoints as $coordinates) { $points[] = Point::fromArray($coordinates); } $curves[] = $this->getMockForAbstractClass(Curve::class, [$points]); } return $curves; } /** * @return array<mixed> */ public function providerValidComponents(): array { return [ 'empty' => [[]], 'two curves with two points' => [[[[0, 0], [1, 1]], [[2, 2], [3, 3]]]], 'two curves, second is empty' => [[[[0, 0], [1, 1]], []]], ]; } /** * @dataProvider providerValidComponents * @covers ::__construct * * @param array<?array<array<int|null>>> $points */ public function testConstructor(array $points): void { $stub = $this->getMockForAbstractClass(MultiCurve::class, [$this->createCurves($points)]); $this->assertNotNull($stub); } /** * @covers ::__construct */ public function testConstructorWrongComponentTypeThrowsException(): void { $this->expectException(InvalidGeometryException::class); $this->expectExceptionMessageMatches('/Cannot construct .+Curve.*\. Expected .+Curve components, got.+/'); // @phpstan-ignore-next-line $this->getMockForAbstractClass(MultiCurve::class, [[new Point(), new Point()]]); } /** * @covers ::__construct * @covers ::geometryType * @covers ::dimension */ public function testGeometryType(): void { $stub = $this->getMockForAbstractClass(MultiCurve::class, []); $this->assertEquals(Geometry::MULTI_CURVE, $stub->geometryType()); $this->assertInstanceOf(MultiCurve::class, $stub); $this->assertInstanceOf(\geoPHP\Geometry\MultiGeometry::class, $stub); $this->assertInstanceOf(\geoPHP\Geometry\Collection::class, $stub); $this->assertInstanceOf(\geoPHP\Geometry\Geometry::class, $stub); $this->assertSame(1, $stub->dimension()); } /** * @return array<mixed> */ public function providerIsClosed(): array { return [ 'empty' => [[], false], 'two ring' => [[[[0, 0], [1, 1], [0, 0]], [[2, 2], [3, 3], [2, 2]]], true], 'two curve forming a ring' => [[[[0, 0], [1, 1], [2, 2]], [[2, 2], [3, 3], [0, 0]]], false], 'two curves, second is not closed' => [[[[0, 0], [1, 1], [0, 0]], [[2, 2], [3, 3]]], false], ]; } /** * @dataProvider providerIsClosed * @covers ::isClosed * * @param array<mixed> $components */ public function testIsClosed(array $components, bool $isClosed): void { $stub = $this->getMockForAbstractClass(MultiCurve::class, [$this->createCurves($components)]); $this->assertSame($isClosed, $stub->isClosed()); } /** * @return array<mixed> */ public function providerBoundary(): array { return [ 'empty' => [ [], new MultiPoint() ], 'two curves' => [ [[[1, 1], [2, 2]], [[5, 5], [6, 6]]], MultiPoint::fromArray([[1, 1], [2, 2], [5, 5], [6, 6]]) ], 'two curves, second is closed' => [ [[[1, 1], [2, 2]], [[5, 5], [5, 5]]], MultiPoint::fromArray([[1, 1], [2, 2]]) ], 'connecting curves, "mod 2 rule"' => [ [[[1, 1], [2, 2]], [[2, 2], [3, 3]]], MultiPoint::fromArray([[1, 1], [3, 3]]) ], 'complex example' => [ [[[1, 1], [2, 2]], [[2, 2], [3, 3], [4, 4], [4, 4], [3, 3], [5, 5]]], MultiPoint::fromArray([[1, 1], [5, 5]]) ], ]; } /** * @dataProvider providerBoundary * @covers ::boundary * * @param array<mixed> $components */ public function testBoundary(array $components, Geometry $expectedBoundary): void { $stub = $this->getMockForAbstractClass(MultiCurve::class, [$this->createCurves($components)]); $this->assertEquals($expectedBoundary, $stub->boundary()); } } <?php namespace geoPHP\Tests\Unit\Geometry; use geoPHP\Exception\InvalidGeometryException; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\LineString; use geoPHP\Geometry\Point; use PHPUnit\Framework\TestCase; /** * Unit tests of LineString geometry * * @group geometry * @coversDefaultClass geoPHP\Geometry\LineString * * @uses geoPHP\Geometry\Point * @uses geoPHP\Geometry\GeometryCollection */ class LineStringTest extends TestCase { public const DELTA = 1e-8; /** * @param array<array<int>> $coordinateArray * @return Point[] */ private function createPoints(array $coordinateArray): array { $points = []; foreach ($coordinateArray as $point) { $points[] = Point::fromArray($point); } return $points; } /** * @return array<string, array<array<array<int|null>>>> */ public function providerValidComponents(): array { return [ 'empty' => [[]], 'with two points' => [[[0, 0], [1, 1]]], 'LineString Z' => [[[0, 0, 0], [1, 1, 1]]], 'LineString M' => [[[0, 0, null, 0], [1, 1, null, 1]]], 'LineString ZM' => [[[0, 0, 0, 0], [1, 1, 1, 1]]], 'LineString with 5 points' => [[[0, 0], [1, 1], [2, 2], [3, 3], [4, 4]]], ]; } /** * @dataProvider providerValidComponents * @covers ::__construct * * @param array<array<int|null>> $points */ public function testConstructor(array $points): void { $lineString = new LineString($this->createPoints($points)); $this->assertNotNull($lineString); $this->assertInstanceOf(LineString::class, $lineString); } /** * @covers ::__construct */ public function testConstructorEmptyComponentThrowsException(): void { $this->expectException(InvalidGeometryException::class); $this->expectExceptionMessageMatches('/Cannot create a collection of empty Points.+/'); // Empty points new LineString([new Point(), new Point(), new Point()]); } /** * @covers ::__construct */ public function testConstructorNonArrayComponentTypeError(): void { $this->expectException(\TypeError::class); $this->expectErrorMessageMatches('/Argument #?1 .+ type array, string given/'); // @phpstan-ignore-next-line new LineString('foo'); } /** * @covers ::__construct */ public function testConstructorSinglePointThrowsException(): void { $this->expectException(InvalidGeometryException::class); $this->expectExceptionMessageMatches('/Cannot construct a [a-zA-Z_\\\\]+LineString with a single point/'); new LineString([new Point(1, 2)]); } /** * @covers ::__construct */ public function testConstructorWrongComponentTypeThrowsException(): void { $this->expectException(InvalidGeometryException::class); $this->expectExceptionMessageMatches('/Cannot construct .+LineString\. Expected .+Point components, got.+/'); // @phpstan-ignore-next-line new LineString([new LineString(), new LineString()]); } /** * @covers ::fromArray */ public function testFromArray(): void { $this->assertEquals( LineString::fromArray([[1,2,3,4], [5,6,7,8]]), new LineString([new Point(1, 2, 3, 4), new Point(5, 6, 7, 8)]) ); } /** * @covers ::geometryType */ public function testGeometryType(): void { $line = new LineString(); $this->assertEquals(LineString::LINE_STRING, $line->geometryType()); $this->assertInstanceOf(LineString::class, $line); $this->assertInstanceOf(\geoPHP\Geometry\Curve::class, $line); $this->assertInstanceOf(\geoPHP\Geometry\Collection::class, $line); $this->assertInstanceOf(\geoPHP\Geometry\Geometry::class, $line); } /** * @covers ::isEmpty */ public function testIsEmpty(): void { $line1 = new LineString(); $this->assertTrue($line1->isEmpty()); $line2 = new LineString($this->createPoints([[1,2], [3,4]])); $this->assertFalse($line2->isEmpty()); } /** * @covers ::dimension */ public function testDimension(): void { $this->assertSame(1, (new LineString())->dimension()); } /** * @dataProvider providerValidComponents * @covers ::numPoints * * @param array<array<int|null>> $points */ public function testNumPoints(array $points): void { $line = new LineString($this->createPoints($points)); $this->assertCount($line->numPoints(), $points); } /** * @dataProvider providerValidComponents * @covers ::pointN * * @param array<array<int|null>> $points */ public function testPointN(array $points): void { $components = $this->createPoints($points); $line = new LineString($components); $this->assertNull($line->pointN(0)); for ($i = 1; $i < count($components); $i++) { // positive n $this->assertEquals($components[$i - 1], $line->pointN($i)); // negative n $this->assertEquals($components[count($components) - $i], $line->pointN(-$i)); } } /** * @return array<string, array{mixed, Point}> */ public function providerCentroid(): array { return [ 'empty LineString' => [[], new Point()], 'null coordinates' => [[[0, 0], [0, 0]], new Point(0, 0)], '↗ vector' => [[[0, 0], [1, 1]], new Point(0.5, 0.5)], '↙ vector' => [[[0, 0], [-1, -1]], new Point(-0.5, -0.5)], 'random geographical coordinates' => [[ [20.0390625, -16.97274101999901], [-11.953125, 17.308687886770034], [0.703125, 52.696361078274485], [30.585937499999996, 52.696361078274485], [42.5390625, 41.77131167976407], [-13.359375, 38.8225909761771], [18.984375, 17.644022027872726] ], new Point(8.71798087550578, 31.1304531386738)], 'crossing the antimeridian' => [[[170, 47], [-170, 47]], new Point(0, 47)] ]; } /** * @dataProvider providerCentroid * @covers ::centroid * @covers ::getCentroidAndLength * * @param array<array<int|null>> $points * @param Point $centroidPoint */ public function testCentroid(array $points, Point $centroidPoint): void { $line = LineString::fromArray($points); $centroid = $line->centroid(); $this->assertEqualsWithDelta($centroidPoint, $centroid, self::DELTA); } /** * @return array<string, array{mixed, bool}> */ public function providerIsSimple(): array { return [ 'simple' => [[[0, 0], [0, 10]], true], 'self-crossing' => [[[0, 0], [10, 0], [10, 10], [0, -10]], false], // 'self-tangent' => // [[[0, 0], [10, 0], [-10, 0]], false, 'faulty'], // FIXME: isSimple() fails to check self-tangency ]; } /** * @dataProvider providerIsSimple * @covers ::isSimple * * @param array<array<int|null>> $points * @param bool $result */ public function testIsSimple(array $points, bool $result, ?string $skip = null): void { $line = LineString::fromArray($points); if ($skip === 'faulty') { $this->markTestIncomplete("Current implementation has known problems with self tangency."); } $this->assertSame($result, $line->isSimple()); } /** * @return array<string, array{mixed, bool}> */ public function providerIsRing(): array { return [ 'empty' => [[], false], 'non closed' => [[[0, 0], [0, 10], [10, 10]], false], 'simple ring' => [[[0, 0], [0, 10], [10, 10], [0, 0]], true], 'self-crossing' => [[[0, 0], [10, 0], [10, 10], [0, -10], [0, 0]], false], ]; } /** * @dataProvider providerIsRing * @covers ::isRing * * @param array<array<int|null>> $points * @param bool $result */ public function testIsRing(array $points, bool $result, ?string $skip = null): void { $line = LineString::fromArray($points); $this->assertSame($result, $line->isRing()); } /** * @return array<array{array<mixed>, float}> */ public function providerLength(): array { return [ [[[0, 0], [10, 0]], 10.0], [[[1, 1], [2, 2], [2, 3.5], [1, 3], [1, 2], [2, 1]], 6.44646111349608], ]; } /** * @dataProvider providerLength * @covers ::length * * @param array<mixed> $points * @param float $result */ public function testLength(array $points, float $result): void { $line = LineString::fromArray($points); $this->assertEqualsWithDelta($result, $line->length(), self::DELTA); // Results of Lengh and Length3D should be equal on 2D dataset. $this->assertEquals($line->length(), $line->length3D()); } /** * @return array<array{array<mixed>, float}> */ public function providerLength3D(): array { return [ [[[0, 0, 0], [10, 0, 10]], 14.142135623731], [[[1, 1, 0], [2, 2, 2], [2, 3.5, 0], [1, 3, 2], [1, 2, 0], [2, 1, 2]], 11.926335310544], ]; } /** * @dataProvider providerLength3D * @covers ::length3D * * @param array<mixed> $points * @param float $result */ public function testLength3D(array $points, float $result): void { $line = LineString::fromArray($points); $this->assertEqualsWithDelta($result, $line->length3D(), self::DELTA); } /** * @return array<array{array<mixed>, array<string, float>}> */ public function providerLengths(): array { return [ [[[0, 0], [0, 0]], [ 'greatCircle' => 0.0, 'haversine' => 0.0, 'vincenty' => 0.0, 'PostGIS' => 0.0 ]], [[[0, 0], [10, 0]], [ 'greatCircle' => 1113194.9079327357, 'haversine' => 1113194.9079327371, 'vincenty' => 1113194.9079322326, 'PostGIS' => 1113194.90793274 ]], [[[0, 0, 0], [10, 0, 5000]], [ 'greatCircle' => 1113206.136817154, 'haversine' => 1113194.9079327371, 'vincenty' => 1113194.9079322326, 'PostGIS' => 1113194.90793274 ]], [[[0, 47], [10, 47]], [ 'greatCircle' => 758681.06593496865, 'haversine' => 758681.06593497901, 'vincenty' => 760043.0186457854, 'postGIS' => 760043.018642104 ]], [[[1, 1, 0], [2, 2, 2], [2, 3.5, 0], [1, 3, 2], [1, 2, 0], [2, 1, 2]], [ 'greatCircle' => 717400.38999229996, 'haversine' => 717400.38992081373, 'vincenty' => 714328.06433538091, 'postGIS' => 714328.064406871 ]], [[[19, 47], [19.000001, 47], [19.000001, 47.000001], [19.000001, 47.000002], [19.000002, 47.000002]], [ 'greatCircle' => 0.37447839912084818, 'haversine' => 0.36386002147417207, 'vincenty' => 0.37445330532190713, 'postGIS' => 0.374453678675281 ]] ]; } /** * @dataProvider providerLengths * @covers ::greatCircleLength * * @param array<mixed> $points * @param array<string, float> $results */ public function testGreatCircleLength(array $points, array $results): void { $line = LineString::fromArray($points); $this->assertEqualsWithDelta($results['greatCircle'], $line->greatCircleLength(), self::DELTA); } /** * @dataProvider providerLengths * @covers ::haversineLength * * @param array<mixed> $points * @param array<string, float> $results */ public function testHaversineLength(array $points, array $results): void { $line = LineString::fromArray($points); $this->assertEqualsWithDelta($results['haversine'], $line->haversineLength(), self::DELTA); } /** * @dataProvider providerLengths * @covers ::vincentyLength * * @param array<mixed> $points * @param array<string, float> $results */ public function testVincentyLength(array $points, array $results): void { $line = LineString::fromArray($points); $this->assertEqualsWithDelta($results['vincenty'], $line->vincentyLength(), self::DELTA); } /** * @covers ::vincentyLength */ public function testVincentyLengthAntipodalPoints(): void { $line = LineString::fromArray([[-89.7, 0], [89.7, 0]]); $this->expectException(\Exception::class); $line->vincentyLength(); } /** * @covers ::explode */ public function testExplode(): void { $point1 = new Point(1, 2); $point2 = new Point(3, 4); $point3 = new Point(5, 6); $line = new LineString([$point1, $point2, $point3]); $this->assertEquals( [new LineString([$point1, $point2]), new LineString([$point2, $point3])], $line->explode() ); $this->assertSame( [[$point1, $point2], [$point2, $point3]], $line->explode(true) ); $this->assertSame([], (new LineString())->explode()); $this->assertSame([], (new LineString())->explode(true)); } /** * @return array<string, array{Geometry, float}> */ public function providerDistance(): array { return [ 'Point on vertex' => [new Point(0, 10), 0.0], 'Point, closest distance is 10' => [new Point(10, 10), 10.0], 'LineString, same points' => [LineString::fromArray([[0, 10], [10, 10]]), 0.0], 'LineString, closest distance is 10' => [LineString::fromArray([[10, 10], [20, 20]]), 10.0], 'intersecting line' => [LineString::fromArray([[-10, 5], [10, 5]]), 0.0], 'GeometryCollection' => [new \geoPHP\Geometry\GeometryCollection([LineString::fromArray([[10, 10], [20, 20]])]), 10.0], // TODO: test other types ]; } /** * @dataProvider providerDistance * @covers ::distance */ public function testDistance(Geometry $otherGeometry, float $expectedDistance): void { $line = LineString::fromArray([[0, 0], [0, 10]]); $this->assertSame($expectedDistance, $line->distance($otherGeometry)); } /** * @return array<string, array{array<mixed>, array<string, ?float>}> */ public function providerElevation(): array { return [ '2D' => [ [[0, 0], [1, 1], [2, 2], [3, 3]], [ 'minZ' => null, 'maxZ' => null, 'minM' => null, 'maxM' => null, 'zDiff' => null, ] ], '4D' => [ [[0, 0, 100.0, 0.0], [1, 1, 50.0, -0.5], [2, 2, 150.0, -1.0], [3, 3, 75.0, 0.5]], [ 'minZ' => 50.0, 'maxZ' => 150.0, 'minM' => -1.0, 'maxM' => 0.5, 'zDiff' => 25.0, ] ], ]; } /** * @dataProvider providerElevation * @covers ::minimumZ * @covers ::maximumZ * @covers ::minimumM * @covers ::maximumM * @covers ::zDifference * * @param array<array<int>> $points * @param array<string, float> $results */ public function testMinimumAndMaximumZAndMAndDifference($points, $results): void { $line = LineString::fromArray($points); $this->assertSame($results['minZ'], $line->minimumZ()); $this->assertSame($results['maxZ'], $line->maximumZ()); $this->assertSame($results['minM'], $line->minimumM()); $this->assertSame($results['maxM'], $line->maximumM()); $this->assertSame($results['zDiff'], $line->zDifference()); } /** * @return array<array<float>> [tolerance, gain, loss] */ public function providerElevationGainAndLossByTolerance(): array { return [ [0.0, 50.0, 30.0], [5.0, 48.0, 28.0], [15.0, 36.0, 16.0] ]; } /** * @dataProvider providerElevationGainAndLossByTolerance * @covers ::elevationGain * @covers ::elevationLoss */ public function testElevationGainAndLoss(?float $tolerance, float $gain, float $loss): void { $line = LineString::fromArray( [ [0, 0, 100], [0, 0, 102], [0, 0, 105], [0, 0, 103], [0, 0, 110], [0, 0, 118], [0, 0, 102], [0, 0, 108], [0, 0, 102], [0, 0, 108], [0, 0, 102], [0, 0, 120], ] ); $this->assertSame($gain, $line->elevationGain($tolerance)); $this->assertSame($loss, $line->elevationLoss($tolerance)); } /** * @covers ::elevationGain * @covers ::elevationLoss */ public function testElevationGainAndLoss2D(): void { $line = LineString::fromArray([[1, 2], [3, 4], [5, 6]]); $this->assertSame(0.0, $line->elevationGain()); $this->assertSame(0.0, $line->elevationLoss()); } } <?php /** * This file contains the CollectionTest class. * For more information see the class description below. * * @author Peter Bathory <peter.bathory@cartographia.hu> * @since 2020-03-19 */ namespace geoPHP\Tests\Unit\Geometry; use geoPHP\Exception\InvalidGeometryException; use geoPHP\Geometry\Collection; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\LineString; use geoPHP\Geometry\MultiPoint; use geoPHP\Geometry\Point; use geoPHP\Geometry\Polygon; use PHPUnit\Framework\TestCase; /** * Unit tests of abstracts Collection class * * @coversDefaultClass geoPHP\Geometry\Collection * * @uses geoPHP\Geometry\Point * @uses geoPHP\Geometry\LineString */ class CollectionTest extends TestCase { /** * @return array<array{Geometry[], string}> */ public function providerConstructorAllowedComponentType(): array { return [ [[Point::fromArray([1, 2])], Point::class], [[LineString::fromArray([[1, 2], [3, 4]])], LineString::class], [ [LineString::fromArray([[1, 2], [3, 4]]), MultiPoint::fromArray([[1, 2], [3, 4]])], Collection::class, ] ]; } /** * @dataProvider providerConstructorAllowedComponentType * @covers ::__construct * * @param Geometry[] $components * @param string $allowedComponentType */ public function testConstructorAllowedComponentTypeParameter(array $components, string $allowedComponentType): void { $this->assertNotNull( $this->getMockForAbstractClass(Collection::class, [$components, $allowedComponentType]) ); // Not allowed component type $this->expectException(InvalidGeometryException::class); $this->expectExceptionMessageMatches('/^Cannot construct .+\. Expected .+Polygon components, got .+\.$/'); $this->getMockForAbstractClass(Collection::class, [$components, Polygon::class]); } public function testConstructorWrongComponent(): void { // Not allowed component type $this->expectException(InvalidGeometryException::class); $this->expectExceptionMessageMatches('/^Cannot construct .+\. Expected .+Geometry components, got string\.$/'); $this->getMockForAbstractClass(Collection::class, [[new Point(), 'foo'], Geometry::class, true]); } /** * @covers ::__construct */ public function testConstructorAllowEmptyParameter(): void { $emptyComponent = new Point(); $component = new Point(1, 2); // Allowed empty, given non empty $this->assertNotNull( $this->getMockForAbstractClass(Collection::class, [[$component], Geometry::class, true]) ); // Allowed empty, given empty $this->assertNotNull( $this->getMockForAbstractClass(Collection::class, [[$emptyComponent], Geometry::class, true]) ); // Not allowed empty, given non empty $this->assertNotNull( $this->getMockForAbstractClass(Collection::class, [[$component], Geometry::class, false]) ); // Not allowed empty, given empty throws exception $this->expectException(InvalidGeometryException::class); $this->expectExceptionMessage('Cannot create a collection of empty Points (1. component)'); $this->getMockForAbstractClass(Collection::class, [[$emptyComponent], Geometry::class, false]); } /** * @return array<string, array{Geometry[], bool}> */ public function providerIs3D(): array { return [ '2D' => [[new Point(1, 2)], false], '3D' => [[new Point(1, 2, 3)], true], 'mixed' => [[new Point(1, 2, 3), new Point(1, 2)], true], ]; } /** * @dataProvider providerIs3D * @covers ::is3D * @covers ::__construct * * @param Geometry[] $components * @param bool $result */ public function testIs3D(array $components, bool $result): void { /** @var Collection $stub */ $stub = $this->getMockForAbstractClass(Collection::class, [$components, Geometry::class, true]); $this->assertEquals($result, $stub->is3D()); } /** * @return array<array{Geometry[], bool}> */ public function providerIsMeasured(): array { return [ [[new Point()], false], [[new Point(1, 2)], false], [[new Point(1, 2, 3)], false], [[new Point(1, 2, 3, 4)], true], [[new Point(1, 2, 3, 4), new Point(1, 2)], true], ]; } /** * @dataProvider providerIsMeasured * @covers ::isMeasured * @covers ::__construct * * @param Geometry[] $components * @param bool $result */ public function testIsMeasured(array $components, bool $result): void { /** @var Collection $stub */ $stub = $this->getMockForAbstractClass(Collection::class, [$components, Geometry::class, true]); $this->assertEquals($result, $stub->isMeasured()); } /** * @dataProvider providerIsMeasured * @dataProvider providerInvertXY * @covers ::getComponents * * @param Geometry[] $components */ public function testGetComponents(array $components): void { /** @var Collection $stub */ $stub = $this->getMockForAbstractClass(Collection::class, [$components, Geometry::class, true]); $this->assertEquals($components, $stub->getComponents()); } /** * @return array<array{Geometry[], Geometry[]}> */ public function providerInvertXY(): array { return [ [ [new Point(1, 2, 3)], [new Point(2, 1, 3)] ], [ [LineString::fromArray([[1, 2, 3], [5, 6, 7]]), Point::fromArray([10, 11, 12])], [LineString::fromArray([[2, 1, 3], [6, 5, 7]]), Point::fromArray([11, 10, 12])], ], ]; } /** * @dataProvider providerInvertXY * @covers ::invertXY * * @param Geometry[] $components */ public function testInvertXY(array $components): void { /** @var Collection */ $collection = $this->getMockForAbstractClass(Collection::class, [$components]); /** @var Collection */ $expectedCollection = $this->getMockForAbstractClass(Collection::class, [$components]); $inverse = $collection->invertXY(); // InvertXY returns the inverted geometry $this->assertEquals($expectedCollection, $inverse); // invertXY() alters the original geometry $this->assertSame($collection, $inverse); // Must be symmetric, invertXY()->invertXY() gives the original geometry $this->assertEquals($expectedCollection, $inverse->invertXY()->invertXY()); } /** * @covers ::flatten */ public function testFlatten(): void { $components = [ new Point(1, 2, 3, 4), new Point(5, 6, 7, 8), new LineString([new Point(1, 2, 3, 4), new Point(5, 6, 7, 8)]), ]; /** @var Collection $stub */ $stub = $this->getMockForAbstractClass(Collection::class, [$components]); $stub->flatten(); $this->assertFalse($stub->is3D()); $this->assertFalse($stub->isMeasured()); $this->assertFalse($stub->getPoints()[0]->is3D()); } /** * @return array<array{array<Geometry>, bool}> */ public function providerIsEmpty(): array { return [ [[], true], [[new Point()], true], [[new Point(1, 2)], false], ]; } /** * @dataProvider providerIsEmpty * @covers ::isEmpty * * @param Geometry[] $components * @param bool $result */ public function testIsEmpty(array $components, bool $result): void { /** @var Collection $stub */ $stub = $this->getMockForAbstractClass(Collection::class, [$components, Geometry::class, true]); $this->assertEquals($result, $stub->isEmpty()); } /** * @covers ::x * @covers ::y * @covers ::z * @covers ::m */ public function testNonApplicableMethods(): void { /** @var Collection $stub */ $stub = $this->getMockForAbstractClass(Collection::class, [[], Geometry::class, true]); $this->assertNull($stub->x()); $this->assertNull($stub->y()); $this->assertNull($stub->z()); $this->assertNull($stub->m()); } /** * @covers ::asArray */ public function testAsArray(): void { $components = [ new Point(1, 2), new LineString(), LineString::fromArray([[1, 2, 3], [5, 6, 7]]) ]; $expected = [ [1, 2], [], [[1, 2, 3], [5, 6, 7]], ]; /** @var Collection $stub */ $stub = $this->getMockForAbstractClass(Collection::class, [$components, Geometry::class, true]); $this->assertEquals($expected, $stub->asArray()); } /** * @dataProvider providerConstructorAllowedComponentType * @covers ::numGeometries * * @param array<mixed> $components */ public function testNumGeometries(array $components): void { /** @var Collection $stub */ $stub = $this->getMockForAbstractClass(Collection::class, [$components]); $this->assertEquals(count($components), $stub->numGeometries()); } /** * @dataProvider providerConstructorAllowedComponentType * @covers ::GeometryN * * @param array<mixed> $components */ public function testGeometryN(array $components): void { /** @var Collection $stub */ $stub = $this->getMockForAbstractClass(Collection::class, [$components]); for ($i = 0; $i < count($components) + 1; ++$i) { $this->assertEquals($components[$i] ?? null, $stub->geometryN($i + 1)); } } /** * @return array<string, array<int|Geometry[]>> */ public function providerNumPoints(): array { return [ 'no components' => [0, []], 'linestring' => [2, [LineString::fromArray([[1, 2], [3, 4]])]], 'linestring, point' => [3, [LineString::fromArray([[1, 2], [3, 4]]), new Point(5, 6)]], ]; } /** * @dataProvider providerNumPoints * @covers ::numPoints * * @param Geometry[]|array{} $components */ public function testNumPoints(int $expectedNum, array $components): void { /** @var Collection $stub */ $stub = $this->getMockForAbstractClass(Collection::class, [$components]); $this->assertEquals($expectedNum, $stub->numPoints()); } /** * @return array<string, array<int|Geometry[]>> */ public function providerGetPoints(): array { return [ 'no components' => [[], []], 'linestring, point' => [ [LineString::fromArray([[1, 2], [3, 4]]), new Point(5, 6)], [new Point(1, 2), new Point(3, 4), new Point(5, 6)] ], ]; } /** * @dataProvider providerGetPoints * @covers ::getPoints * * @param Geometry[]|array{} $components * @param Point[]|array{} $expectedPoints */ public function testGetPoints(array $components, array $expectedPoints): void { /** @var Collection $stub */ $stub = $this->getMockForAbstractClass(Collection::class, [$components]); $this->assertEquals($expectedPoints, $stub->getPoints()); } /** * @covers ::explode */ public function testExplode(): void { $points = [new Point(1, 2), new Point(3, 4), new Point(5, 6), new Point(1, 2)]; $components = [ new \geoPHP\Geometry\Polygon([new LineString($points)]) ]; /** @var Collection $stub */ $stub = $this->getMockForAbstractClass(Collection::class, [$components]); $segments = $stub->explode(); $this->assertCount(count($points) - 1, $segments); foreach ($segments as $i => $segment) { $this->assertCount(2, $segment->getComponents()); $this->assertSame($points[$i], $segment->startPoint()); $this->assertSame($points[$i + 1], $segment->endPoint()); } } /** * @return array<string, array{Geometry[], Geometry, ?float}> */ public function providerDistance(): array { return [ "collection of points to point" => [ [new Point(1, 1), new Point(2, 1)], new Point(2, 0), 1.0 ], "collection of points to touching point" => [ [new Point(1, 1), new Point(2, 1)], new Point(2, 1), 0.0 ], "collection of points to touching point in between" => [ [new Point(1, 1), new Point(2, 1)], new Point(1.5, 1), 0.5 ], "collection of points to empty point" => [ [new Point(1, 1), new Point(2, 2)], new Point(), null ], "collection of points with one empty point to point" => [ [new Point(1, 0), new Point()], new Point(2, 0), 1.0 ], "collection of points to linestring" => [ [new Point(1, 1), new Point(2, 1)], LineString::fromArray([[0, 0], [2, 0]]), 1.0 ], // . | . // | "collection of points to crossing linestring" => [ [new Point(0, 0), new Point(2, 0)], LineString::fromArray([[1, -1], [1, 1]]), 1.0 ], ]; } /** * @dataProvider providerDistance * @covers ::distance * * @param Geometry[] $components * @param Geometry $otherGeometry * @param float|null $expectedDistance */ public function testDistance(array $components, Geometry $otherGeometry, ?float $expectedDistance): void { /** @var Collection $stub */ $stub = $this->getMockForAbstractClass(Collection::class, [$components, Geometry::class, true]); $this->assertSame($expectedDistance, $stub->distance($otherGeometry)); } } <?php namespace geoPHP\Tests\Unit\Geometry; use geoPHP\Exception\InvalidGeometryException; use geoPHP\Geometry\Curve; use geoPHP\Geometry\LineString; use geoPHP\Geometry\MultiPoint; use geoPHP\Geometry\Point; use PHPUnit\Framework\TestCase; /** * Unit tests of Curve abstract geometry * * @group geometry * @coversDefaultClass geoPHP\Geometry\Curve * * @uses geoPHP\Geometry\Point * @uses geoPHP\Geometry\LineString * @uses geoPHP\Geometry\MultiPoint */ class CurveTest extends TestCase { public const DELTA = 1e-8; /** * @param array<array<int|float>> $coordinateArray * @return Point[] */ private function createPoints(array $coordinateArray): array { $points = []; foreach ($coordinateArray as $point) { $points[] = Point::fromArray($point); } return $points; } /** * @return array<string, array<array<array<int|null>>>> */ public function providerValidComponents(): array { return [ 'empty' => [[]], 'with two points' => [[[0, 0], [1, 1]]], 'CURVE Z' => [[[0, 0, 0], [1, 1, 1]]], 'CURVE M' => [[[0, 0, null, 0], [1, 1, null, 1]]], 'CURVE ZM' => [[[0, 0, 0, 0], [1, 1, 1, 1]]], 'CURVE of 5 points' => [[[0, 0], [1, 1], [2, 2], [3, 3], [4, 4]]], ]; } /** * @dataProvider providerValidComponents * @covers ::__construct * * @param array<array<int|null>> $points */ public function testConstructor(array $points): void { $curveStub = $this->getMockForAbstractClass(Curve::class, [$this->createPoints($points)]); $this->assertNotNull($curveStub); } /** * @covers ::__construct */ public function testConstructorEmptyComponentThrowsException(): void { $this->expectException(InvalidGeometryException::class); $this->expectExceptionMessageMatches('/Cannot create a collection of empty Points.+/'); // Empty points $this->getMockForAbstractClass(Curve::class, [[new Point(), new Point(), new Point()]]); } /** * @covers ::__construct */ public function testConstructorSinglePointThrowsException(): void { $this->expectException(InvalidGeometryException::class); $this->expectExceptionMessageMatches('/Cannot construct a [a-zA-Z_\\\\]+Curve.* with a single point/'); $this->getMockForAbstractClass(Curve::class, [[new Point(1, 2)]]); } /** * @covers ::__construct */ public function testConstructorWrongComponentTypeThrowsException(): void { $this->expectException(InvalidGeometryException::class); $this->expectExceptionMessageMatches('/Cannot construct .+Curve.*\. Expected .+Point components, got.+/'); // @phpstan-ignore-next-line $this->getMockForAbstractClass(Curve::class, [[new LineString(), new LineString()]]); } /** * @covers ::geometryType */ public function testGeometryType(): void { $curveStub = $this->getMockForAbstractClass(Curve::class, []); $this->assertEquals(LineString::CURVE, $curveStub->geometryType()); $this->assertInstanceOf(Curve::class, $curveStub); $this->assertInstanceOf(\geoPHP\Geometry\Collection::class, $curveStub); $this->assertInstanceOf(\geoPHP\Geometry\Geometry::class, $curveStub); } /** * @return array<array{array<mixed>, MultiPoint}> */ public function providerBoundary(): array { return [ 'Empty' => [[], new MultiPoint()], 'Closed' => [[[1, 2], [3, 4], [1, 2]], new MultiPoint()], '3 points' => [[[1, 2], [3, 4], [5, 6]], new MultiPoint([new Point(1, 2), new Point(5, 6)])], ]; } /** * @dataProvider providerBoundary * @covers ::boundary * * @param array<mixed> $components * @param MultiPoint $expectedBoundary */ public function testBoundary(array $components, MultiPoint $expectedBoundary): void { $curveStub = $this->getMockForAbstractClass(Curve::class, [$this->createPoints($components)]); $this->assertEquals($expectedBoundary, $curveStub->boundary()); } /** * @return array<array{array<mixed>, ?Point, ?Point}> */ public function providerStartEndPoint(): array { return [ 'Empty' => [[], null, null], '3 points' => [[[1, 2], [3, 4], [5, 6]], new Point(1, 2), new Point(5, 6)], 'Closed' => [[[1, 2], [3, 4], [1, 2]], new Point(1, 2), new Point(1, 2)], ]; } /** * @dataProvider providerStartEndPoint * @covers ::startPoint * @covers ::endPoint * * @param array<mixed> $components * @param Point|null $expectedStartPoint * @param Point|null $expectedEndPoint */ public function testStartEndPoint(array $components, ?Point $expectedStartPoint, ?Point $expectedEndPoint): void { $curveStub = $this->getMockForAbstractClass(Curve::class, [$this->createPoints($components)]); $this->assertEquals($expectedStartPoint, $curveStub->startPoint()); $this->assertEquals($expectedEndPoint, $curveStub->endPoint()); } /** * @return array<array{array<mixed>, bool}> */ public function providerIsClosed(): array { return [ 'Empty' => [[], false], '3 points' => [[[1, 2], [3, 4], [5, 6]], false], 'Closed' => [[[1, 2], [3, 4], [1, 2]], true], 'Enough close' => [[[1, 2], [3, 4], [1.0000000001, 2.0000000001]], true], 'Not enough close' => [[[1, 2], [3, 4], [1.00001, 2.00001]], false], ]; } /** * @dataProvider providerIsClosed * @covers ::isClosed * * @param array<mixed> $components * @param bool $isClosed */ public function testIsClosed(array $components, bool $isClosed): void { $curveStub = $this->getMockForAbstractClass(Curve::class, [$this->createPoints($components)]); $this->assertEquals($isClosed, $curveStub->isClosed()); } /** * @return array<array{array<mixed>, bool}> */ public function providerIsRing(): array { return [ 'Empty' => [[], false], '3 points' => [[[1, 2], [3, 4], [5, 6]], false], 'Closed' => [[[1, 2], [3, 4], [1, 2]], true], 'Enough close' => [[[1, 2], [3, 4], [1.0000000001, 2.0000000001]], true], 'Not enough close' => [[[1, 2], [3, 4], [1.00001, 2.00001]], false], ]; } /** * @dataProvider providerIsClosed * @covers ::isClosed * * @param array<mixed> $components * @param bool $isClosed */ public function testIsRing(array $components, bool $isClosed): void { $curveStub = $this->getMockForAbstractClass(Curve::class, [$this->createPoints($components)]); $this->assertEquals($isClosed, $curveStub->isClosed()); } /** * @dataProvider providerValidComponents * @covers ::getPoints * * @param array<mixed> $components */ public function testGetPoints(array $components): void { $componentPoints = $this->createPoints($components); $curveStub = $this->getMockForAbstractClass(Curve::class, [$componentPoints]); $this->assertEquals($componentPoints, $curveStub->getPoints()); } /** * @covers ::area * @covers ::exteriorRing * @covers ::numInteriorRings * @covers ::interiorRingN */ public function testTrivialMethods(): void { $stub = $this->getMockForAbstractClass(Curve::class, [[]]); $this->assertSame(0.0, $stub->area()); $this->assertSame(null, $stub->exteriorRing()); $this->assertSame(null, $stub->numInteriorRings()); $this->assertSame(null, $stub->interiorRingN(1)); } } <?php namespace geoPHP\Tests\Unit\Legacy; use geoPHP\Geometry\Geometry; use geoPHP\geoPHP; use PHPUnit\Framework\TestCase; class GeosTest extends TestCase { public function testGeos(): void { if (!geoPHP::isGeosInstalled()) { $this->markTestSkipped('GEOS not installed'); } foreach (scandir('tests/input') as $file) { $parts = explode('.', $file); if ($parts[0]) { if ($parts[0] == 'countries_ne_110m') { // Due to a bug in GEOS we have to skip some tests // It drops TopologyException for valid geometries // https://trac.osgeo.org/geos/ticket/737 // continue; } $format = $parts[1]; $value = file_get_contents('tests/input/' . $file); //echo "\nloading: " . $file . " for format: " . $format; $geometry = geoPHP::load($value, $format); $geosMethods = [ ['name' => 'getGeos'], ['name' => 'flushGeosCache'], ['name' => 'pointOnSurface'], ['name' => 'equals', 'argument' => $geometry], ['name' => 'equalsExact', 'argument' => $geometry], ['name' => 'relate', 'argument' => $geometry], ['name' => 'checkValidity'], ['name' => 'isSimple'], ['name' => 'buffer', 'argument' => '10'], ['name' => 'intersection', 'argument' => $geometry], ['name' => 'convexHull'], ['name' => 'difference', 'argument' => $geometry], ['name' => 'symDifference', 'argument' => $geometry], ['name' => 'union', 'argument' => $geometry], ['name' => 'simplify', 'argument' => '0'], ['name' => 'disjoint', 'argument' => $geometry], ['name' => 'touches', 'argument' => $geometry], ['name' => 'intersects', 'argument' => $geometry], ['name' => 'crosses', 'argument' => $geometry], ['name' => 'within', 'argument' => $geometry], ['name' => 'contains', 'argument' => $geometry], ['name' => 'overlaps', 'argument' => $geometry], ['name' => 'covers', 'argument' => $geometry], ['name' => 'coveredBy', 'argument' => $geometry], ['name' => 'distance', 'argument' => $geometry], ['name' => 'hausdorffDistance', 'argument' => $geometry], ]; foreach ($geosMethods as $method) { $argument = null; $methodName = $method['name']; if (isset($method['argument'])) { $argument = $method['argument']; } $errorMessage = 'Failed on "' . $methodName . '" method with test file "' . $file . '"'; // GEOS don't like empty points if ($geometry->geometryType() == 'Point' && $geometry->isEmpty()) { continue; } switch ($methodName) { case 'geos': $this->assertInstanceOf('GEOSGeometry', $geometry->$methodName($argument), $errorMessage); break; case 'equals': case 'equalsExact': case 'disjoint': case 'touches': case 'intersects': case 'crosses': case 'within': case 'contains': case 'overlaps': case 'covers': case 'coveredBy': $this->assertIsBool($geometry->$methodName($argument), $errorMessage); break; case 'pointOnSurface': case 'buffer': case 'intersection': case 'convexHull': case 'difference': case 'symDifference': case 'union': case 'simplify': $this->assertInstanceOf(Geometry::class, $geometry->$methodName($argument), $errorMessage); break; case 'distance': case 'hausdorffDistance': $this->assertIsFloat($geometry->$methodName($argument), $errorMessage); break; case 'relate': $this->assertMatchesRegularExpression( '/[0-9TF]{9}/', $geometry->$methodName($argument), $errorMessage ); break; case 'checkValidity': $this->assertArrayHasKey('valid', $geometry->$methodName($argument), $errorMessage); break; case 'isSimple': if ($geometry->geometryType() == 'GeometryCollection') { $this->assertNull($geometry->$methodName($argument), $errorMessage); } else { $this->assertNotNull($geometry->$methodName($argument), $errorMessage); } break; default: } } } } } } <?php namespace geoPHP\Tests\Unit\Legacy; use geoPHP\geoPHP; use PHPUnit\Framework\TestCase; class AdaptersTest extends TestCase { public function testAdapters(): void { foreach (scandir('tests/input') as $file) { $parts = explode('.', $file); if ($parts[0]) { $format = $parts[1]; $input = file_get_contents('tests/input/' . $file); //echo "\nloading: " . $file . " for format: " . $format; $geometry = geoPHP::load($input, $format); // Test adapter output and input. Do a round-trip and re-test foreach (geoPHP::getAdapterMap() as $adapterKey => $adapterClass) { if ($adapterKey == 'google_geocode') { //Don't test google geocoder regularly. Comment to test continue; } $output = $geometry->out($adapterKey); $this->assertNotNull($output, "Empty output on " . $adapterKey); if ($output) { $adapterName = 'geoPHP\\Adapter\\' . $adapterClass; /** @var \geoPHP\Adapter\GeoAdapter $adapterLoader */ $adapterLoader = new $adapterName(); $testGeom1 = $adapterLoader->read($output); $testGeom2 = $adapterLoader->read($testGeom1->out($adapterKey)); $this->assertEquals( $testGeom1->out('wkt'), $testGeom2->out('wkt'), "Mismatched adapter output in " . $adapterClass . ' (test file: ' . $file . ')' ); } } // Test to make sure adapter work the same wether GEOS is ON or OFF // Cannot test methods if GEOS is not intstalled if (!geoPHP::isGeosInstalled()) { return; } foreach (geoPHP::getAdapterMap() as $adapterKey => $adapterClass) { if ($adapterKey === 'google_geocode') { //Don't test google geocoder regularly. Comment to test continue; } // Turn GEOS on geoPHP::enableGeos(); $output = $geometry->out($adapterKey); if ($output) { $adapterName = 'geoPHP\\Adapter\\' . $adapterClass; $adapterLoader = new $adapterName(); $testGeom1 = $adapterLoader->read($output); // Turn GEOS off geoPHP::disableGeos(); $testGeom2 = $adapterLoader->read($output); // Turn GEOS back On geoPHP::enableGeos(); // Check to make sure a both are the same with geos and without $msg = "Mismatched adapter output between GEOS and NORM in " . $adapterClass . ' (test file: ' . $file . ')'; $this->assertEquals($testGeom1->out('wkt'), $testGeom2->out('wkt'), $msg); } } } } } } <?php namespace geoPHP\Tests\Unit\Legacy; use geoPHP\geoPHP; use geoPHP\Geometry\Geometry; use PHPUnit\Framework\TestCase; // FIXME file 20120702.gpx contains one MultiLineString // but methodTester() also wants to test Points and LineStrings (ie does nothing) class File20120702Test extends TestCase { public function testMethods(): void { $format = 'gpx'; $value = file_get_contents('tests/input/20120702.gpx'); $geometry = geoPHP::load($value, $format); $methods = [ ['name' => 'area'], ['name' => 'boundary'], ['name' => 'getBBox'], ['name' => 'centroid'], ['name' => 'length'], ['name' => 'greatCircleLength', 'argument' => 6378137], ['name' => 'haversineLength'], ['name' => 'y'], ['name' => 'x'], ['name' => 'numGeometries'], ['name' => 'geometryN', 'argument' => '1'], ['name' => 'startPoint'], ['name' => 'endPoint'], ['name' => 'isRing'], ['name' => 'isClosed'], ['name' => 'numPoints'], ['name' => 'pointN', 'argument' => '1'], ['name' => 'exteriorRing'], ['name' => 'numInteriorRings'], ['name' => 'interiorRingN', 'argument' => '1'], ['name' => 'dimension'], ['name' => 'geometryType'], ['name' => 'SRID'], ['name' => 'setSRID', 'argument' => '4326'], ]; foreach ($methods as $method) { $argument = null; $methodName = $method['name']; if (isset($method['argument'])) { $argument = $method['argument']; } $this->methodsTester($geometry, $methodName, $argument); } } /** * @param Geometry $geometry * @param string $methodName * @param mixed $argument */ private function methodsTester(Geometry $geometry, string $methodName, $argument): void { if (!method_exists($geometry, $methodName)) { $this->fail("Method " . $methodName . '() doesn\'t exists.'); } $failedOnMessage = $geometry->geometryType() . ' failed on ' . $methodName ; switch ($methodName) { case 'y': case 'x': if ($geometry->geometryType() == 'Point') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'geometryN': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'startPoint': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'endPoint': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'isRing': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'isClosed': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'pointN': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'exteriorRing': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'numInteriorRings': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'interiorRingN': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'setSRID': //TODO: The method setSRID() should return TRUE. break; case 'SRID': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'getBBox': if ($geometry->geometryType() == 'Point') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'centroid': if ($geometry->geometryType() == 'Point') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'length': if ($geometry->geometryType() == 'Point') { $this->assertEquals($geometry->$methodName($argument), 0, $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertEquals( $geometry->$methodName($argument), (float) '0.11624637315233', $failedOnMessage ); } break; case 'numGeometries': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'numPoints': if ($geometry->geometryType() == 'Point') { $this->assertEquals($geometry->$methodName($argument), 1, $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'dimension': if ($geometry->geometryType() == 'Point') { $this->assertEquals($geometry->$methodName($argument), 0, $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertEquals($geometry->$methodName($argument), 1, $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertEquals($geometry->$methodName($argument), 1, $failedOnMessage); } break; case 'boundary': if ($geometry->geometryType() == 'Point') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'greatCircleLength': if ($geometry->geometryType() == 'Point') { $this->assertEquals($geometry->$methodName($argument), 0, $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotEquals($geometry->$methodName($argument), '9500.9359867418', $failedOnMessage); } break; case 'haversineLength': case 'area': $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); break; case 'geometryType': $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); break; default: $this->assertTrue($geometry->$methodName($argument), $failedOnMessage); } } } <?php namespace geoPHP\Tests\Unit\Legacy; use geoPHP\Geometry\Geometry; use geoPHP\geoPHP; use PHPUnit\Framework\TestCase; class MethodsTest extends TestCase { public function testMethods(): void { foreach (scandir('tests/input') as $file) { $parts = explode('.', $file); if ($parts[0]) { $format = $parts[1]; $value = file_get_contents('tests/input/' . $file); //echo "\nloading: " . $file . " for format: " . $format; $geometry = geoPHP::load($value, $format); $methods = [ ['name' => 'area', 'argument' => false ], ['name' => 'boundary'], ['name' => 'getBBox'], ['name' => 'centroid'], ['name' => 'length'], ['name' => 'greatCircleLength', 'argument' => '1'], ['name' => 'haversineLength'], ['name' => 'y'], ['name' => 'x'], ['name' => 'numGeometries'], ['name' => 'geometryN', 'argument' => '1'], ['name' => 'startPoint'], ['name' => 'endPoint'], ['name' => 'isRing'], ['name' => 'isClosed'], ['name' => 'numPoints'], ['name' => 'pointN', 'argument' => '1'], ['name' => 'exteriorRing'], ['name' => 'numInteriorRings'], ['name' => 'interiorRingN', 'argument' => '1'], ['name' => 'dimension'], ['name' => 'geometryType'], ['name' => 'SRID'], ['name' => 'setSRID', 'argument' => '4326'], ]; foreach ($methods as $method) { $argument = null; $methodName = $method['name']; if (isset($method['argument'])) { $argument = $method['argument']; } $this->methodsTester($geometry, $methodName, $argument, $file); } $this->methodsTesterWithGeos($geometry); } } } /** * @param Geometry $geometry * @param string $methodName * @param string|array<mixed> $argument * @param string $file */ private function methodsTester(Geometry $geometry, string $methodName, $argument, string $file): void { if (!method_exists($geometry, $methodName)) { $this->fail("Method " . $methodName . '() doesn\'t exists.'); } $failedOnMessage = 'Failed on ' . $methodName . ' (test file: ' . $file . ', geometry type: ' . $geometry->geometryType() . ')'; switch ($methodName) { case 'y': case 'x': if (!$geometry->isEmpty()) { if ($geometry->geometryType() == 'Point') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } } break; case 'geometryN': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'startPoint': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'endPoint': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'isRing': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'isClosed': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'pointN': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'exteriorRing': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'numInteriorRings': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'interiorRingN': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'SRID': break; case 'getBBox': if (!$geometry->isEmpty()) { if ($geometry->geometryType() == 'Point') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } } break; case 'centroid': if ($geometry->geometryType() == 'Point') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'length': if ($geometry->geometryType() == 'Point') { $this->assertEquals($geometry->$methodName($argument), 0, $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotEquals($geometry->$methodName($argument), 0, $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotEquals($geometry->$methodName($argument), 0, $failedOnMessage); } break; case 'numGeometries': if ($geometry->geometryType() == 'Point') { $this->assertNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'numPoints': if ($geometry->geometryType() == 'Point') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'dimension': if ($geometry->geometryType() == 'Point') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'boundary': if ($geometry->geometryType() == 'Point') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'LineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } if ($geometry->geometryType() == 'MultiLineString') { $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); } break; case 'haversineLength': //TODO: Check if output is a float >= 0. //TODO: Sometimes haversineLength() returns NAN, needs to check why. break; case 'greatCircleLength': $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); break; case 'area': $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); break; case 'geometryType': $this->assertNotNull($geometry->$methodName($argument), $failedOnMessage); break; case 'setSRID': //TODO: The method setSRID() should return TRUE. break; default: $this->assertTrue($geometry->$methodName($argument), $failedOnMessage); } } /** * @param \geoPHP\Geometry\Geometry $geometry * @throws \Exception */ private function methodsTesterWithGeos($geometry): void { // Cannot test methods if GEOS is not intstalled if (!geoPHP::isGeosInstalled()) { return; } $methods = [ 'boundary', 'envelope', 'getBBox', 'x', 'y', 'startPoint', 'endPoint', 'isRing', 'isClosed', 'numPoints', ]; foreach ($methods as $method) { // Turn GEOS on geoPHP::enableGeos(); $geosResult = $geometry->$method(); // Turn GEOS off geoPHP::disableGeos(); $normResult = $geometry->$method(); // Turn GEOS back On geoPHP::enableGeos(); $geosType = gettype($geosResult); $normType = gettype($normResult); if ($geosType != $normType) { //var_dump($geosType, $normType); $this->fail('Type mismatch on ' . $method); } // Now check base on type if ($geosType == 'object') { /** @var Geometry $geosResult */ /** @var Geometry $normResult */ $hausDist = $geosResult->hausdorffDistance(geoPHP::load($normResult->out('wkt'), 'wkt')); // Get the length of the diagonal of the bbox - this is used to scale the haustorff distance // Using Pythagorean theorem $bb = $geosResult->getBBox(); $scale = $bb ? sqrt((($bb['maxy'] - $bb['miny']) ^ 2) + (($bb['maxx'] - $bb['minx']) ^ 2)) : 1; // The difference in the output of GEOS and native-PHP methods // should be less than 0.5 scaled haustorff units if ($hausDist / $scale > 0.5) { //var_dump('GEOS : ', $geosResult->out('wkt'), 'NORM : ', $normResult->out('wkt')); $this->fail('Output mismatch on ' . $method); } } if ($geosType == 'boolean' || $geosType == 'string') { if ($geosResult !== $normResult) { //var_dump('GEOS : ', $geosResult->out('wkt'), 'NORM : ', $normResult->out('wkt')); $this->fail('Output mismatch on ' . $method); } } //@@TODO: Run tests for output of types arrays and float //@@TODO: centroid function is non-compliant for collections and strings } } } <?php namespace geoPHP\Tests\Unit\Adapter; use geoPHP\Adapter\WKT; use geoPHP\Exception\FileFormatException; use geoPHP\Geometry\{ Geometry, GeometryCollection, Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon }; use PHPUnit\Framework\TestCase; /** * Test cases for reading capabilities of WKT adapter * * @coversDefaultClass geoPHP\Adapter\WKT * * @uses geoPHP\Geometry\Point * @uses geoPHP\Geometry\MultiPoint * @uses geoPHP\Geometry\LineString * @uses geoPHP\Geometry\MultiLineString * @uses geoPHP\Geometry\Polygon * @uses geoPHP\Geometry\MultiPolygon * @uses geoPHP\Geometry\GeometryCollection * @uses geoPHP\Geometry\Collection * @uses geoPHP\Geometry\Curve * @uses geoPHP\Geometry\Surface * @uses geoPHP\Geometry\MultiGeometry * @uses geoPHP\Geometry\MultiCurve * @uses geoPHP\Geometry\MultiSurface * @uses geoPHP\Exception\FileFormatException */ class WKTReaderTest extends TestCase { /** * @var WKT */ private static $wktAdapter; /** * Instantiate WKT adapter * * @return void */ public static function setUpBeforeClass(): void { self::$wktAdapter = new WKT(); } /** * @dataProvider providerValidPoint * @dataProvider providerValidLineString * @dataProvider providerValidPolygon * @dataProvider providerValidMultiPoint * @dataProvider providerValidMultiLineString * @dataProvider providerValidMultiPolygon * @dataProvider providerValidGeometryCollection * * @covers ::read * @covers ::getWktType * @covers ::parseTypeAndGetData * @covers ::parseCoordinates * @covers ::parsePoint * @covers ::parseLineString * @covers ::parsePolygon * @covers ::parseMultiPoint * @covers ::parseMultiLineString * @covers ::parseMultiPolygon * @covers ::parseGeometryCollection */ public function testValidWkt(string $wkt, Geometry $expectedGeometry): void { $geometry = self::$wktAdapter->read($wkt); $this->assertEquals($expectedGeometry, $geometry); } /** * @dataProvider providerInvalidWkt * @dataProvider providerInvalidPoint * @dataProvider providerInvalidLineString * @dataProvider providerInvalidPolygon * @dataProvider providerInvalidMultiPoint * @dataProvider providerInvalidMultiLineString * @dataProvider providerInvalidMultiPolygon * @dataProvider providerInvalidGeometryCollection * * @covers ::read * @covers ::getWktType * @covers ::parseTypeAndGetData * @covers ::parseCoordinates * @covers ::parsePoint * @covers ::parseLineString * @covers ::parsePolygon * @covers ::parseMultiPoint * @covers ::parseMultiLineString * @covers ::parseMultiPolygon * @covers ::parseGeometryCollection */ public function testInvalidWktThrowsException(string $wkt): void { $this->expectException(FileFormatException::class); print_r(self::$wktAdapter->read($wkt)); } /** * @return array<string, array<string>> */ public function providerInvalidWkt() { return [ 'empty string' => [''], 'non wkt string' => ['lorem ipsum'], 'invalid geometry name' => ['FOO (1 2)'], ]; } /** * @return array<array{string, Geometry}> */ public function providerValidPoint() { return [ [ 'POINT EMPTY', new Point(), ], [ 'POINT(1 2)', new Point(1, 2), ], [ 'POINT ( 1 2 )', new Point(1, 2), ], [ 'POINT ( 1 2 )', new Point(1, 2), ], [ 'POINT (1.0123456789 2.0123456789)', new Point(1.0123456789, 2.0123456789), ], [ 'POINT Z (1 2 3)', new Point(1, 2, 3), ], [ 'POINTZ(1 2 3)', new Point(1, 2, 3), ], [ 'POINT (1 2 3)', new Point(1, 2, 3), ], [ 'POINT M (1 2 3)', new Point(1, 2, null, 3), ], [ 'POINTM(1 2 3)', new Point(1, 2, null, 3), ], [ 'POINT (1 2 3 4)', new Point(1, 2, 3, 4), ], [ 'POINT ZM (1 2 3 4)', new Point(1, 2, 3, 4), ], [ 'POINTZM (1 2 3 4)', new Point(1, 2, 3, 4), ], // Mismatched coordinate dimension but we are following Geos's tolerant reader. [ 'POINT Z (1 2 3 4)', new Point(1, 2, 3), ], [ 'POINT M (1 2 3 4)', new Point(1, 2, null, 3), ], ]; } /** * @return array<array<string>> */ public function providerInvalidPoint() { return [ [ 'POINT', ], [ 'POINT ()', ], [ 'POINT foo', ], [ 'POINT (1)', ], [ 'POINT (1 2', ], [ 'POINT (a b)', ], [ 'POINT (null null)', ], [ 'POINT (1 2 z)', ], // Coordinate dimension mismatch [ 'POINT Z (1 2)', ], [ 'POINT M (1 2)', ], [ 'POINT ZM (1 2)', ], [ 'POINT ZM (1 2 3)', ], ]; } /** * @return array<array{string, Geometry}> */ public function providerValidLineString() { return [ [ 'LINESTRING EMPTY', new LineString(), ], [ 'LINESTRING(1 2,5 6)', LineString::fromArray([[1, 2], [5, 6]]), ], [ 'LINESTRING ( 1 2, 5 6 )', LineString::fromArray([[1, 2], [5, 6]]), ], [ 'LINESTRING (1 2, 5 6, 10 11, 15 16)', LineString::fromArray([[1, 2], [5, 6], [10, 11], [15, 16]]), ], [ 'LINESTRING (1.0123456789 2.0123456789, 5.987654321 6.987654321)', LineString::fromArray([[1.0123456789, 2.0123456789], [5.987654321, 6.987654321]]), ], [ 'LINESTRING Z (1 2 3, 5 6 7)', LineString::fromArray([[1, 2, 3], [5, 6, 7]]), ], [ 'LINESTRINGZ(1 2 3,5 6 7)', LineString::fromArray([[1, 2, 3], [5, 6, 7]]), ], [ 'LINESTRING (1 2 3, 5 6 7)', LineString::fromArray([[1, 2, 3], [5, 6, 7]]), ], [ 'LINESTRING M (1 2 3, 5 6 7)', LineString::fromArray([[1, 2, null, 3], [5, 6, null, 7]]), ], [ 'LINESTRINGM(1 2 3,5 6 7)', LineString::fromArray([[1, 2, null, 3], [5, 6, null, 7]]), ], [ 'LINESTRING ZM (1 2 3 4, 5 6 7 8)', LineString::fromArray([[1, 2, 3, 4], [5, 6, 7, 8]]), ], [ 'LINESTRINGZM(1 2 3 4,5 6 7 8)', LineString::fromArray([[1, 2, 3, 4], [5, 6, 7, 8]]), ], [ 'LINESTRING (1 2 3 4, 5 6 7 8)', LineString::fromArray([[1, 2, 3, 4], [5, 6, 7, 8]]), ], // Mismatched coordinate dimension but we are following Geos's tolerant reader. [ 'LINESTRING Z (1 2 3 4, 5 6 7 8)', LineString::fromArray([[1, 2, 3], [5, 6, 7]]), ], [ 'LINESTRING M (1 2 3 4, 5 6 7 8)', LineString::fromArray([[1, 2, null, 3], [5, 6, null, 7]]), ], ]; } /** * @return array<array<string>> */ public function providerInvalidLineString() { return [ [ 'LINESTRING', ], [ 'LINESTRING ()', ], [ 'LINESTRING (1)', ], [ 'LINESTRING (1 2)', ], [ 'LINESTRING (a b, c d)', ], [ 'LINESTRING (null null, null null)', ], [ 'LINESTRING (1 2 z, 5 6 z)', ], // Coordinate dimension mismatch [ 'LINESTRING Z (1 2, 5 6)', ], [ 'LINESTRING M (1 2, 5 6)', ], [ 'LINESTRING ZM (1 2, 5 6)', ], [ 'LINESTRING ZM (1 2 3, 5 6 7)', ], ]; } /** * @return array<array{string, Geometry}> */ public function providerValidPolygon() { return [ [ 'POLYGON EMPTY', new Polygon(), ], [ 'POLYGON((11 12,21 22,31 32,11 12))', Polygon::fromArray([[[11, 12], [21, 22], [31, 32], [11, 12]]]), ], [ 'POLYGON ( ( 11 12, 21 22, 31 32, 11 12 ) )', Polygon::fromArray([[[11, 12], [21, 22], [31, 32], [11, 12]]]), ], [ 'POLYGON ((11 12, 21 22, 31 32, 11 12), (111 112, 121 122, 131 132, 111 112))', Polygon::fromArray( [ [[11, 12], [21, 22], [31, 32], [11, 12]], [[111, 112], [121, 122], [131, 132], [111, 112]] ] ), ], [ 'POLYGONZ((11 12 13,21 22 23,31 32 33,11 12 43))', Polygon::fromArray([[[11, 12, 13], [21, 22, 23], [31, 32, 33], [11, 12, 43]]]), ], [ 'POLYGON Z ( ( 11 12 13, 21 22 23, 31 32 33, 11 12 43 ) )', Polygon::fromArray([[[11, 12, 13], [21, 22, 23], [31, 32, 33], [11, 12, 43]]]), ], [ 'POLYGON ((11 12 13, 21 22 23, 31 32 33, 11 12 43), ' . '(111 112 113, 121 122 123, 131 132 133, 111 112 143))', Polygon::fromArray( [ [[11, 12, 13], [21, 22, 23], [31, 32, 33], [11, 12, 43]], [[111, 112, 113], [121, 122, 123], [131, 132, 133], [111, 112, 143]] ] ), ], [ 'POLYGONM((11 12 13,21 22 23,31 32 33,11 12 43))', Polygon::fromArray([[[11, 12, null, 13], [21, 22, null, 23], [31, 32, null, 33], [11, 12, null, 43]]]), ], [ 'POLYGON M ( ( 11 12 13, 21 22 23, 31 32 33, 11 12 43 ) )', Polygon::fromArray([[[11, 12, null, 13], [21, 22, null, 23], [31, 32, null, 33], [11, 12, null, 43]]]), ], [ 'POLYGONZM((11 12 13 14,21 22 23 24,31 32 33 34,11 12 43 44))', Polygon::fromArray([[[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34], [11, 12, 43, 44]]]), ], [ 'POLYGON ZM ( ( 11 12 13 14, 21 22 23 24, 31 32 33 34, 11 12 43 44 ) )', Polygon::fromArray([[[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34], [11, 12, 43, 44]]]), ], [ 'POLYGON ((11 12 13 14, 21 22 23 24, 31 32 33 34, 11 12 43 44), ' . '(111 112 113 114, 121 122 123 124, 131 132 133 134, 111 112 143 144))', Polygon::fromArray( [ [[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34], [11, 12, 43, 44]], [[111, 112, 113, 114], [121, 122, 123, 124], [131, 132, 133, 134], [111, 112, 143, 144]] ] ), ], ]; } /** * @return array<string, array<string>> */ public function providerInvalidPolygon() { return [ 'Polygon no data text' => [ 'POLYGON', ], 'Polygon empty components' => [ 'POLYGON ()', ], 'Polygon empty ring' => [ 'POLYGON (())', ], 'Polygon non closed ring' => [ 'POLYGON ((11 12, 21 22, 31 32, 41 42))', ], 'Polygon missing )' => [ 'POLYGON ((11 12, 21 22, 31 32, 11 12)', ], 'Polygon missing , between coordinates' => [ 'POLYGON ((11 12 21 22, 31 32, 11 12))', ], 'Polygon empty second ring' => [ 'POLYGON ((11 12, 21 22, 31 32, 11 12), ())', ], ]; } /** * Test cases for MultiPoint. * * There are two common interpretation and geoPHP should support both: * MULTIPOINT ((1 2), (3 4)) * MULTIPOINT (1 2, 3 4) * * @return array<array{string, Geometry}> */ public function providerValidMultiPoint(): array { return [ [ 'MULTIPOINT EMPTY', new MultiPoint(), ], [ 'MULTIPOINT(1 2)', MultiPoint::fromArray([[1, 2]]), ], [ 'MULTIPOINT((1 2))', MultiPoint::fromArray([[1, 2]]), ], [ 'MULTIPOINT (EMPTY, 1 2)', MultiPoint::fromArray([[], [1, 2]]), ], [ 'MULTIPOINT ((1 2), EMPTY)', MultiPoint::fromArray([[1, 2], []]), ], [ 'MULTIPOINT(1 2,5 6)', MultiPoint::fromArray([[1, 2], [5, 6]]), ], [ 'MULTIPOINT((1 2),(5 6))', MultiPoint::fromArray([[1, 2], [5, 6]]), ], [ 'MULTIPOINT ( 1 2, 5 6 )', MultiPoint::fromArray([[1, 2], [5, 6]]), ], [ 'MULTIPOINT ( ( 1 2 ), ( 5 6 ) )', MultiPoint::fromArray([[1, 2], [5, 6]]), ], [ 'MULTIPOINT (1 2, 5 6, 10 11, 15 16)', MultiPoint::fromArray([[1, 2], [5, 6], [10, 11], [15, 16]]), ], [ 'MULTIPOINT ((1 2), (5 6), (10 11), (15 16))', MultiPoint::fromArray([[1, 2], [5, 6], [10, 11], [15, 16]]), ], [ 'MULTIPOINT Z (1 2 3, 5 6 7)', MultiPoint::fromArray([[1, 2, 3], [5, 6, 7]]), ], [ 'MULTIPOINTZ(1 2 3,5 6 7)', MultiPoint::fromArray([[1, 2, 3], [5, 6, 7]]), ], [ 'MULTIPOINT (1 2 3, 5 6 7)', MultiPoint::fromArray([[1, 2, 3], [5, 6, 7]]), ], [ 'MULTIPOINT M (1 2 3, 5 6 7)', MultiPoint::fromArray([[1, 2, null, 3], [5, 6, null, 7]]), ], [ 'MULTIPOINTM(1 2 3, 5 6 7)', MultiPoint::fromArray([[1, 2, null, 3], [5, 6, null, 7]]), ], [ 'MULTIPOINT ZM (1 2 3 4, 5 6 7 8)', MultiPoint::fromArray([[1, 2, 3, 4], [5, 6, 7, 8]]), ], [ 'MULTIPOINT ZM (1 2 3 4, 5 6 7 8)', MultiPoint::fromArray([[1, 2, 3, 4], [5, 6, 7, 8]]), ], [ 'MULTIPOINT (1 2 3 4, 5 6 7 8)', MultiPoint::fromArray([[1, 2, 3, 4], [5, 6, 7, 8]]), ], // Mismatched coordinate dimension but we are following Geos's tolerant reader. [ 'MULTIPOINT Z (1 2 3 4, 5 6 7 8)', MultiPoint::fromArray([[1, 2, 3], [5, 6, 7]]), ], [ 'MULTIPOINT M (1 2 3 4, 5 6 7 8)', MultiPoint::fromArray([[1, 2, null, 3], [5, 6, null, 7]]), ], ]; } /** * @return array<array<string>> */ public function providerInvalidMultiPoint() { return [ [ 'MULTIPOINT', ], [ 'MULTIPOINT ()', ], [ 'MULTIPOINT (1)', ], [ 'MULTIPOINT ((1 2, 3 4))', ], [ 'MULTIPOINT ((1 2), 3 4)', ], ]; } /** * @return array<array{string, Geometry}> */ public function providerValidMultiLineString() { return [ [ 'MULTILINESTRING EMPTY', new MultiLineString(), ], [ 'MULTILINESTRING ( ( 11 12, 21 22, 31 32, 11 12 ) )', MultiLineString::fromArray([[[11, 12], [21, 22], [31, 32], [11, 12]]]), ], [ 'MULTILINESTRING((11 12,21 22),(31 32,11 12))', MultiLineString::fromArray([[[11, 12], [21, 22]], [[31, 32], [11, 12]]]), ], [ 'MULTILINESTRING (EMPTY, (31 32, 11 12))', MultiLineString::fromArray([[], [[31, 32], [11, 12]]]), ], [ 'MULTILINESTRING ((11 12, 21 22), EMPTY)', MultiLineString::fromArray([[[11, 12], [21, 22]], []]), ], [ 'MULTILINESTRING ( (11 12, 21 22, 31 32, 11 12), (111 112, 121 122, 131 132, 111 112) )', MultiLineString::fromArray( [ [[11, 12], [21, 22], [31, 32], [11, 12]], [[111, 112], [121, 122], [131, 132], [111, 112]] ] ), ], [ 'MULTILINESTRING Z ((11 12 13,21 22 23),(31 32 33,11 12 43))', MultiLineString::fromArray([[[11, 12, 13], [21, 22, 23]], [[31, 32, 33], [11, 12, 43]]]), ], [ 'MULTILINESTRING ((11 12 13, 21 22 23), (31 32 33, 11 12 43))', MultiLineString::fromArray([[[11, 12, 13], [21, 22, 23]], [[31, 32, 33], [11, 12, 43]]]), ], [ 'MULTILINESTRING M ((11 12 13,21 22 23), (31 32 33,11 12 43))', MultiLineString::fromArray( [[[11, 12, null, 13], [21, 22, null, 23]], [[31, 32, null, 33], [11, 12, null, 43]]] ), ], [ 'MULTILINESTRING ZM ((11 12 13 14, 21 22 23 24), (31 32 33 34, 11 12 43 44))', MultiLineString::fromArray( [[[11, 12, 13, 14], [21, 22, 23, 24]], [[31, 32, 33, 34], [11, 12, 43, 44]]] ), ], [ 'MULTILINESTRING ((11 12 13 14, 21 22 23 24), (31 32 33 34, 11 12 43 44))', MultiLineString::fromArray( [[[11, 12, 13, 14], [21, 22, 23, 24]], [[31, 32, 33, 34], [11, 12, 43, 44]]] ), ], ]; } /** * @return array<string, array<string>> */ public function providerInvalidMultiLineString() { return [ 'MultiLineString no data text' => [ 'MULTILINESTRING', ], 'MultiLineString empty components' => [ 'MULTILINESTRING ()', ], 'MultiLineString empty line' => [ 'MULTILINESTRING (())', ], 'MultiLineString missing )' => [ 'MULTILINESTRING ((11 12, 21 22)', ], 'MultiLineString missing , between coordinates' => [ 'MULTILINESTRING ((11 12 21 22, 31 32, 11 12))', ], 'MultiLineString empty second line' => [ 'MULTILINESTRING ((11 12, 21 22, 31 32, 11 12), ())', ], ]; } /** * @return array<array{string, Geometry}> */ public function providerValidMultiPolygon() { return [ 'MultiPolygon empty' => [ 'MULTIPOLYGON EMPTY', new MultiPolygon(), ], 'MultiPolygon one single' => [ 'MULTIPOLYGON(((11 12, 21 22, 31 32, 11 12)))', MultiPolygon::fromArray([[[[11, 12], [21, 22], [31, 32], [11, 12]]]]), ], 'MultiPolygon one single 2' => [ 'MULTIPOLYGON ( ( ( 11 12, 21 22, 31 32, 11 12 ) ) )', MultiPolygon::fromArray([[[[11, 12], [21, 22], [31, 32], [11, 12]]]]), ], 'MultiPolygon one with two rings' => [ 'MULTIPOLYGON (((11 12, 21 22, 31 32, 11 12), (111 112, 121 122, 131 132, 111 112)))', MultiPolygon::fromArray( [[ [[11, 12], [21, 22], [31, 32], [11, 12]], [[111, 112], [121, 122], [131, 132], [111, 112]] ]] ), ], 'MultiPolygon two sinle' => [ 'MULTIPOLYGON (((11 12, 21 22, 31 32, 11 12)), ((111 112, 121 122, 131 132, 111 112)))', MultiPolygon::fromArray( [ [[[11, 12], [21, 22], [31, 32], [11, 12]]], [[[111, 112], [121, 122], [131, 132], [111, 112]]] ] ), ], 'MultiPolygon two, first is empty' => [ 'MULTIPOLYGON (EMPTY, ((111 112, 121 122, 131 132, 111 112)))', MultiPolygon::fromArray( [ [], [[[111, 112], [121, 122], [131, 132], [111, 112]]] ] ), ], 'MultiPolygon two with two rings' => [ 'MULTIPOLYGON (' . '((11 12, 21 22, 31 32, 11 12), (111 112, 121 122, 131 132, 111 112)),' . '((211 212, 221 222, 231 232, 211 212), (311 312, 321 322, 331 332, 311 312))' . ')', MultiPolygon::fromArray( [ [ [[11, 12], [21, 22], [31, 32], [11, 12]], [[111, 112], [121, 122], [131, 132], [111, 112]] ], [ [[211, 212], [221, 222], [231, 232], [211, 212]], [[311, 312], [321, 322], [331, 332], [311, 312]] ], ] ), ], ]; } /** * @return array<string, array<string>> */ public function providerInvalidMultiPolygon() { return [ 'MultiPolygon no data text' => [ 'MULTIPOLYGON', ], 'MultiPolygon empty components' => [ 'MULTIPOLYGON ()', ], 'MultiPolygon empty polygon' => [ 'MULTIPOLYGON (())', ], 'MultiPolygon empty ring' => [ 'MULTIPOLYGON ((()))', ], 'MultiPolygon non closed ring' => [ 'MULTIPOLYGON (((11 12, 21 22, 31 32, 41 42)))', ], 'MultiPolygon missing )' => [ 'MULTIPOLYGON (((11 12, 21 22, 31 32, 11 12))', ], 'MultiPolygon missing , between coordinates' => [ 'MULTIPOLYGON (((11 12 21 22, 31 32, 11 12)))', ], 'MultiPolygon empty second polygon' => [ 'MULTIPOLYGON (((11 12, 21 22, 31 32, 11 12), ()))', ], 'MultiPolygon missing ) between polygons middle' => [ 'MULTIPOLYGON (((11 12, 21 22, 31 32, 11 12), ((111 112, 121 122, 131 132, 111 112)))', ], 'MultiPolygon missing ) between polygons end' => [ 'MULTIPOLYGON (((11 12, 21 22, 31 32, 11 12)), ((111 112, 121 122, 131 132, 111 112', ], ]; } /** * @return array<array{string, Geometry}> */ public function providerValidGeometryCollection() { return [ 'GeometryCollection empty' => [ 'GEOMETRYCOLLECTION EMPTY', new GeometryCollection(), ], 'GeometryCollection one point' => [ 'GEOMETRYCOLLECTION (POINT(1 2))', new GeometryCollection( [new Point(1, 2)] ), ], 'GeometryCollection point, ls' => [ 'GEOMETRYCOLLECTION (POINT(1 2), LINESTRING(1 2, 3 4))', new GeometryCollection( [ new Point(1, 2), LineString::fromArray([[1, 2], [3, 4]]) ] ), ], 'GeometryCollection point, ls empty' => [ 'GEOMETRYCOLLECTION (POINT(1 2), LINESTRING EMPTY)', new GeometryCollection( [ new Point(1, 2), new LineString() ] ), ], 'GeometryCollection multipoly' => [ 'GEOMETRYCOLLECTION (' . ' MULTIPOLYGON (((1 2, 3 4, 5 6, 1 2), (7 8, 9 10, 11 12, 7 8)))' . ')', new GeometryCollection( [ MultiPolygon::fromArray( [[[[1, 2], [3, 4], [5, 6], [1, 2]], [[7, 8], [9, 10], [11, 12], [7, 8]]]] ) ] ), ], 'GeometryCollection all types' => [ 'GEOMETRYCOLLECTION (' . ' POINT(1 2),' . ' LINESTRING(1 2, 3 4),' . ' POLYGON((1 2, 3 4, 5 6, 1 2), (7 8, 9 10, 11 12, 7 8))' . ' MULTIPOINT(1 2, 3 4, 5 6),' . ' MULTIPOINT((1 2), (3 4), (5 6)),' . ' MULTILINESTRING((1 2, 3 4, 5 6, 1 2), (7 8, 9 10, 11 12, 7 8)),' . ' MULTIPOLYGON (((1 2, 3 4, 5 6, 1 2), (7 8, 9 10, 11 12, 7 8)))' . ')', new GeometryCollection( [ new Point(1, 2), LineString::fromArray([[1, 2], [3, 4]]), Polygon::fromArray([[[1, 2], [3, 4], [5, 6], [1, 2]], [[7, 8], [9, 10], [11, 12], [7, 8]]]), MultiPoint::fromArray([[1, 2], [3, 4], [5, 6]]), MultiPoint::fromArray([[1, 2], [3, 4], [5, 6]]), MultiLineString::fromArray( [[[1, 2], [3, 4], [5, 6], [1, 2]], [[7, 8], [9, 10], [11, 12], [7, 8]]] ), MultiPolygon::fromArray( [[[[1, 2], [3, 4], [5, 6], [1, 2]], [[7, 8], [9, 10], [11, 12], [7, 8]]]] ), ] ), ], 'GeometryCollection (point, geometrycollection(ls))' => [ 'GEOMETRYCOLLECTION (POINT(1 2), GEOMETRYCOLLECTION (LINESTRING(1 2, 3 4)))', new GeometryCollection( [ new Point(1, 2), new GeometryCollection([LineString::fromArray([[1, 2], [3, 4]])]) ] ), ], 'GeometryCollection (point, geometrycollection(empty point))' => [ 'GEOMETRYCOLLECTION (POINT(1 2), GEOMETRYCOLLECTION (POINT EMPTY))', new GeometryCollection( [ new Point(1, 2), new GeometryCollection([new Point()]) ] ), ], 'GeometryCollection (point, geometrycollection(multipoint(point, empty)))' => [ 'GEOMETRYCOLLECTION (POINT(1 2), GEOMETRYCOLLECTION (MULTIPOINT ((1 2), EMPTY) ) )', new GeometryCollection( [ new Point(1, 2), new GeometryCollection([MultiPoint::fromArray([[1, 2], []])]) ] ), ], ]; } /** * @return array<string, array<string>> */ public function providerInvalidGeometryCollection() { return [ 'GeometryCollection no data text' => [ 'GEOMETRYCOLLECTION', ], 'GeometryCollection no geometry type' => [ 'GEOMETRYCOLLECTION ((1 2))', ], 'GeometryCollection wrong parenthesis' => [ 'GEOMETRYCOLLECTION (POINT(1 2, LINESTRING(1 2, 3 4))', ], 'GeometryCollection wrong parenthesis 2' => [ 'GEOMETRYCOLLECTION (POINT(1 2), LINESTRING(1 2, 3 4)', ], ]; } } <?php namespace geoPHP\Tests\Unit\Adapter; use geoPHP\Adapter\WKT; use geoPHP\Geometry\{ Geometry, GeometryCollection, Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon }; use PHPUnit\Framework\TestCase; /** * Test cases for reading capabilities of WKT adapter. * * @coversDefaultClass geoPHP\Adapter\WKT * * @uses geoPHP\Geometry\Point * @uses geoPHP\Geometry\MultiPoint * @uses geoPHP\Geometry\LineString * @uses geoPHP\Geometry\MultiLineString * @uses geoPHP\Geometry\Polygon * @uses geoPHP\Geometry\MultiPolygon * @uses geoPHP\Geometry\GeometryCollection * @uses geoPHP\Geometry\Collection * @uses geoPHP\Geometry\Curve * @uses geoPHP\Geometry\Surface * @uses geoPHP\Geometry\MultiGeometry * @uses geoPHP\Geometry\MultiCurve * @uses geoPHP\Geometry\MultiSurface * @uses geoPHP\Exception\FileFormatException */ class WKTWriterTest extends TestCase { /** * @var WKT */ private static $wktAdapter; /** * Instantiate WKT adapter * * @return void */ public static function setUpBeforeClass(): void { self::$wktAdapter = new WKT(); } /** * @dataProvider providerPoint * @dataProvider providerLineString * @dataProvider providerPolygon * @dataProvider providerMultiPoint * @dataProvider providerMultiLineString * @dataProvider providerMultiPolygon * @dataProvider providerGeometryCollection * * @covers ::write * @covers ::extractData * @covers ::writePoint * @covers ::writeLineString * @covers ::writeMulti * @covers ::writeGeometryCollection * * @param string $expectedWkt * @param Geometry $geometry * @return void */ public function testValidWkt(string $expectedWkt, Geometry $geometry): void { $wkt = self::$wktAdapter->write($geometry); $this->assertEquals($expectedWkt, $wkt); } /** * @return array<array{string, Geometry}> */ public function providerPoint(): array { return [ [ 'POINT EMPTY', new Point(), ], [ 'POINT (1 2)', new Point(1, 2), ], [ 'POINT (1.0123456789 2.0123456789)', new Point(1.0123456789, 2.0123456789), ], [ 'POINT Z (1 2 3)', new Point(1, 2, 3), ], [ 'POINT M (1 2 3)', new Point(1, 2, null, 3), ], [ 'POINT ZM (1 2 3 4)', new Point(1, 2, 3, 4), ], ]; } /** * @return array<array{string, Geometry}> */ public function providerLineString(): array { return [ [ 'LINESTRING EMPTY', new LineString(), ], [ 'LINESTRING (1 2, 5 6)', LineString::fromArray([[1, 2], [5, 6]]), ], [ 'LINESTRING (1.0123456789 2.0123456789, 5.987654321 6.987654321)', LineString::fromArray([[1.0123456789, 2.0123456789], [5.987654321, 6.987654321]]), ], [ 'LINESTRING Z (1 2 3, 5 6 7)', LineString::fromArray([[1, 2, 3], [5, 6, 7]]), ], [ 'LINESTRING M (1 2 3, 5 6 7)', LineString::fromArray([[1, 2, null, 3], [5, 6, null, 7]]), ], [ 'LINESTRING ZM (1 2 3 4, 5 6 7 8)', LineString::fromArray([[1, 2, 3, 4], [5, 6, 7, 8]]), ], ]; } /** * @return array<array{string, Geometry}> */ public function providerPolygon(): array { return [ [ 'POLYGON EMPTY', new Polygon(), ], [ 'POLYGON ((11 12, 21 22, 31 32, 11 12))', Polygon::fromArray([[[11, 12], [21, 22], [31, 32], [11, 12]]]), ], [ 'POLYGON ((11 12, 21 22, 31 32, 11 12), (111 112, 121 122, 131 132, 111 112))', Polygon::fromArray( [ [[11, 12], [21, 22], [31, 32], [11, 12]], [[111, 112], [121, 122], [131, 132], [111, 112]] ] ), ], [ 'POLYGON Z ((11 12 13, 21 22 23, 31 32 33, 11 12 43))', Polygon::fromArray([[[11, 12, 13], [21, 22, 23], [31, 32, 33], [11, 12, 43]]]), ], [ 'POLYGON Z ((11 12 13, 21 22 23, 31 32 33, 11 12 43), ' . '(111 112 113, 121 122 123, 131 132 133, 111 112 143))', Polygon::fromArray( [ [[11, 12, 13], [21, 22, 23], [31, 32, 33], [11, 12, 43]], [[111, 112, 113], [121, 122, 123], [131, 132, 133], [111, 112, 143]] ] ), ], [ 'POLYGON M ((11 12 13, 21 22 23, 31 32 33, 11 12 43))', Polygon::fromArray([[[11, 12, null, 13], [21, 22, null, 23], [31, 32, null, 33], [11, 12, null, 43]]]), ], [ 'POLYGON ZM ((11 12 13 14, 21 22 23 24, 31 32 33 34, 11 12 43 44))', Polygon::fromArray([[[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34], [11, 12, 43, 44]]]), ], [ 'POLYGON ZM ((11 12 13 14, 21 22 23 24, 31 32 33 34, 11 12 43 44), ' . '(111 112 113 114, 121 122 123 124, 131 132 133 134, 111 112 143 144))', Polygon::fromArray( [ [[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34], [11, 12, 43, 44]], [[111, 112, 113, 114], [121, 122, 123, 124], [131, 132, 133, 134], [111, 112, 143, 144]] ] ), ], ]; } /** * @return array<array{string, Geometry}> */ public function providerMultiPoint(): array { return [ [ 'MULTIPOINT EMPTY', new MultiPoint(), ], [ 'MULTIPOINT ((1 2))', MultiPoint::fromArray([[1, 2]]), ], [ 'MULTIPOINT ((1 2), (5 6))', MultiPoint::fromArray([[1, 2], [5, 6]]), ], [ 'MULTIPOINT ((1 2), (5 6), (10 11), (15 16))', MultiPoint::fromArray([[1, 2], [5, 6], [10, 11], [15, 16]]), ], [ 'MULTIPOINT Z ((1 2 3), (5 6 7))', MultiPoint::fromArray([[1, 2, 3], [5, 6, 7]]), ], [ 'MULTIPOINT M ((1 2 3), (5 6 7))', MultiPoint::fromArray([[1, 2, null, 3], [5, 6, null, 7]]), ], [ 'MULTIPOINT ZM ((1 2 3 4), (5 6 7 8))', MultiPoint::fromArray([[1, 2, 3, 4], [5, 6, 7, 8]]), ], [ 'MULTIPOINT (EMPTY, (1 2))', MultiPoint::fromArray([[], [1, 2]]), ], ]; } /** * @return array<array{string, Geometry}> */ public function providerMultiLineString(): array { return [ [ 'MULTILINESTRING EMPTY', new MultiLineString(), ], [ 'MULTILINESTRING ((11 12, 21 22, 31 32, 11 12))', MultiLineString::fromArray([[[11, 12], [21, 22], [31, 32], [11, 12]]]), ], [ 'MULTILINESTRING ((11 12, 21 22, 31 32, 11 12), (111 112, 121 122, 131 132, 111 112))', MultiLineString::fromArray( [ [[11, 12], [21, 22], [31, 32], [11, 12]], [[111, 112], [121, 122], [131, 132], [111, 112]] ] ), ], [ 'MULTILINESTRING Z ((11 12 13, 21 22 23), (31 32 33, 11 12 43))', MultiLineString::fromArray([[[11, 12, 13], [21, 22, 23]], [[31, 32, 33], [11, 12, 43]]]), ], [ 'MULTILINESTRING M ((11 12 13, 21 22 23), (31 32 33, 11 12 43))', MultiLineString::fromArray( [[[11, 12, null, 13], [21, 22, null, 23]], [[31, 32, null, 33], [11, 12, null, 43]]] ), ], [ 'MULTILINESTRING ZM ((11 12 13 14, 21 22 23 24), (31 32 33 34, 11 12 43 44))', MultiLineString::fromArray( [[[11, 12, 13, 14], [21, 22, 23, 24]], [[31, 32, 33, 34], [11, 12, 43, 44]]] ), ], ]; } /** * @return array<array{string, Geometry}> */ public function providerMultiPolygon(): array { return [ 'MultiPolygon empty' => [ 'MULTIPOLYGON EMPTY', new MultiPolygon(), ], 'MultiPolygon one single' => [ 'MULTIPOLYGON (((11 12, 21 22, 31 32, 11 12)))', MultiPolygon::fromArray([[[[11, 12], [21, 22], [31, 32], [11, 12]]]]), ], 'MultiPolygon one with two rings' => [ 'MULTIPOLYGON (((11 12, 21 22, 31 32, 11 12), (111 112, 121 122, 131 132, 111 112)))', MultiPolygon::fromArray( [[ [[11, 12], [21, 22], [31, 32], [11, 12]], [[111, 112], [121, 122], [131, 132], [111, 112]] ]] ), ], 'MultiPolygon two sinle' => [ 'MULTIPOLYGON (((11 12, 21 22, 31 32, 11 12)), ((111 112, 121 122, 131 132, 111 112)))', MultiPolygon::fromArray( [ [[[11, 12], [21, 22], [31, 32], [11, 12]]], [[[111, 112], [121, 122], [131, 132], [111, 112]]] ] ), ], 'MultiPolygon two with two rings' => [ 'MULTIPOLYGON (' . '((11 12, 21 22, 31 32, 11 12), (111 112, 121 122, 131 132, 111 112)), ' . '((211 212, 221 222, 231 232, 211 212), (311 312, 321 322, 331 332, 311 312))' . ')', MultiPolygon::fromArray( [ [ [[11, 12], [21, 22], [31, 32], [11, 12]], [[111, 112], [121, 122], [131, 132], [111, 112]] ], [ [[211, 212], [221, 222], [231, 232], [211, 212]], [[311, 312], [321, 322], [331, 332], [311, 312]] ], ] ), ], ]; } /** * @return array<array{string, Geometry}> */ public function providerGeometryCollection(): array { return [ 'GeometryCollection empty' => [ 'GEOMETRYCOLLECTION EMPTY', new GeometryCollection(), ], 'GeometryCollection one point' => [ 'GEOMETRYCOLLECTION (POINT (1 2))', new GeometryCollection( [new Point(1, 2)] ), ], 'GeometryCollection point, ls' => [ 'GEOMETRYCOLLECTION (POINT (1 2), LINESTRING (1 2, 3 4))', new GeometryCollection( [ new Point(1, 2), LineString::fromArray([[1, 2], [3, 4]]) ] ), ], 'GeometryCollection multipoly' => [ 'GEOMETRYCOLLECTION (' . 'MULTIPOLYGON (((1 2, 3 4, 5 6, 1 2), (7 8, 9 10, 11 12, 7 8)))' . ')', new GeometryCollection( [ MultiPolygon::fromArray( [[[[1, 2], [3, 4], [5, 6], [1, 2]], [[7, 8], [9, 10], [11, 12], [7, 8]]]] ) ] ), ], 'GeometryCollection point, ls empty' => [ 'GEOMETRYCOLLECTION (POINT (1 2), LINESTRING EMPTY)', new GeometryCollection( [ new Point(1, 2), new LineString() ] ), ], 'GeometryCollection point, multipoly empty' => [ 'GEOMETRYCOLLECTION (POINT (1 2), MULTIPOLYGON EMPTY)', new GeometryCollection( [ new Point(1, 2), new MultiPolygon() ] ), ], 'GeometryCollection all types' => [ 'GEOMETRYCOLLECTION (' . 'POINT (1 2), ' . 'LINESTRING (1 2, 3 4), ' . 'POLYGON ((1 2, 3 4, 5 6, 1 2), (7 8, 9 10, 11 12, 7 8)), ' . 'MULTIPOINT ((1 2), (3 4), (5 6)), ' . 'MULTILINESTRING ((1 2, 3 4, 5 6, 1 2), (7 8, 9 10, 11 12, 7 8)), ' . 'MULTIPOLYGON (((1 2, 3 4, 5 6, 1 2), (7 8, 9 10, 11 12, 7 8)))' . ')', new GeometryCollection( [ new Point(1, 2), LineString::fromArray([[1, 2], [3, 4]]), Polygon::fromArray([[[1, 2], [3, 4], [5, 6], [1, 2]], [[7, 8], [9, 10], [11, 12], [7, 8]]]), MultiPoint::fromArray([[1, 2], [3, 4], [5, 6]]), MultiLineString::fromArray( [[[1, 2], [3, 4], [5, 6], [1, 2]], [[7, 8], [9, 10], [11, 12], [7, 8]]] ), MultiPolygon::fromArray( [[[[1, 2], [3, 4], [5, 6], [1, 2]], [[7, 8], [9, 10], [11, 12], [7, 8]]]] ), ] ), ], 'GeometryCollection point, geometrycollection(ls)' => [ 'GEOMETRYCOLLECTION (POINT (1 2), GEOMETRYCOLLECTION (LINESTRING (1 2, 3 4)))', new GeometryCollection( [ new Point(1, 2), new GeometryCollection([LineString::fromArray([[1, 2], [3, 4]])]) ] ), ], 'GeometryCollection (point, geometrycollection(ls))' => [ 'GEOMETRYCOLLECTION (POINT (1 2), GEOMETRYCOLLECTION (LINESTRING (1 2, 3 4)))', new GeometryCollection( [ new Point(1, 2), new GeometryCollection([LineString::fromArray([[1, 2], [3, 4]])]) ] ), ], 'GeometryCollection (point, geometrycollection(empty point))' => [ 'GEOMETRYCOLLECTION (POINT (1 2), GEOMETRYCOLLECTION (POINT EMPTY))', new GeometryCollection( [ new Point(1, 2), new GeometryCollection([new Point()]) ] ), ], 'GeometryCollection (point, geometrycollection(multipoint(point, empty)))' => [ 'GEOMETRYCOLLECTION (POINT (1 2), GEOMETRYCOLLECTION (MULTIPOINT ((1 2), EMPTY)))', new GeometryCollection( [ new Point(1, 2), new GeometryCollection([MultiPoint::fromArray([[1, 2], []])]) ] ), ], 'GeometryCollection Z point Z, ls Z' => [ 'GEOMETRYCOLLECTION Z (POINT Z (1 2 10), LINESTRING Z (1 2 20, 3 4 30))', new GeometryCollection( [ new Point(1, 2, 10), LineString::fromArray([[1, 2, 20], [3, 4, 30]]) ] ), ], 'GeometryCollection Z multipoly Z' => [ 'GEOMETRYCOLLECTION Z (' . 'MULTIPOLYGON Z (((1 2 10, 3 4 20, 5 6 30, 1 2 10), (7 8 40, 9 10 50, 11 12 60, 7 8 40)))' . ')', new GeometryCollection( [ MultiPolygon::fromArray( [[ [[1, 2, 10], [3, 4, 20], [5, 6, 30], [1, 2, 10]], [[7, 8, 40], [9, 10, 50], [11, 12, 60], [7, 8, 40]] ]] ) ] ), ], 'GeometryCollection mixed Z point Z, ls' => [ 'GEOMETRYCOLLECTION Z (POINT Z (1 2 10), LINESTRING Z (1 2 0, 3 4 0))', new GeometryCollection( [ new Point(1, 2, 10), LineString::fromArray([[1, 2], [3, 4]]) ] ), ], 'GeometryCollection M point M, ls M' => [ 'GEOMETRYCOLLECTION M (POINT M (1 2 10), LINESTRING M (1 2 20, 3 4 30))', new GeometryCollection( [ new Point(1, 2, null, 10), LineString::fromArray([[1, 2, null, 20], [3, 4, null, 30]]) ] ), ], 'GeometryCollection ZM point ZM, ls ZM' => [ 'GEOMETRYCOLLECTION ZM (POINT ZM (1 2 10 11), LINESTRING ZM (1 2 20 21, 3 4 30 31))', new GeometryCollection( [ new Point(1, 2, 10, 11), LineString::fromArray([[1, 2, 20, 21], [3, 4, 30, 31]]) ] ), ], ]; } } <?php namespace geoPHP\Tests\Unit\Adapter; use geoPHP\Adapter\GeoHash; use PHPUnit\Framework\TestCase; /** * Unit tests of GeoHash adapter * * @coversDefaultClass geoPHP\Adapter\GeoHash */ class GeoHashTest extends TestCase { /** * test cases for adjacent geohashes. * @covers ::adjacent */ public function testAdjacent(): void { $this->assertEquals( 'xne', GeoHash::adjacent('xn7', 'top'), 'Did not find correct top adjacent geohash for xn7' ); $this->assertEquals( 'xnk', GeoHash::adjacent('xn7', 'right'), 'Did not find correct right adjacent geohash for xn7' ); $this->assertEquals( 'xn5', GeoHash::adjacent('xn7', 'bottom'), 'Did not find correct bottom adjacent geohash for xn7' ); $this->assertEquals( 'xn6', GeoHash::adjacent('xn7', 'left'), 'Did not find correct left adjacent geohash for xn7' ); $this->assertEquals( 'xnd', GeoHash::adjacent(GeoHash::adjacent('xn7', 'left'), 'top'), 'Did not find correct top-left adjacent geohash for xn7' ); } } <?php namespace geoPHP\Tests\Unit\Adapter; use geoPHP\Adapter\EWKT; use geoPHP\Geometry\Geometry; use geoPHP\Geometry\Point; use PHPUnit\Framework\TestCase; /** * Test cases for EWKT adapter * * @coversDefaultClass geoPHP\Adapter\EWKT * * @uses geoPHP\Geometry\Point * @uses geoPHP\Geometry\MultiPoint * @uses geoPHP\Geometry\LineString * @uses geoPHP\Geometry\MultiLineString * @uses geoPHP\Geometry\Polygon * @uses geoPHP\Geometry\MultiPolygon * @uses geoPHP\Geometry\GeometryCollection * @uses geoPHP\Geometry\Collection * @uses geoPHP\Geometry\Curve * @uses geoPHP\Geometry\Surface * @uses geoPHP\Geometry\MultiGeometry * @uses geoPHP\Geometry\MultiCurve * @uses geoPHP\Geometry\MultiSurface * @uses geoPHP\Exception\FileFormatException */ class EWKTTest extends TestCase { /** * @dataProvider providerReadValidEwkt * * @covers ::read */ public function testReadingValidEwkt(string $wkt, int $srid): void { $geometry = (new EWKT())->read($wkt); $this->assertInstanceOf(Geometry::class, $geometry); $this->assertEquals($srid, $geometry->getSRID()); } /** * @return array<array{string, int}> */ public function providerReadValidEwkt(): array { return [ [ 'SRID=3857;POINT(1 2)', 3857, ], [ 'SRID=4326;POINT(19.0 47.0 100)', 4326, ], ]; } /** * @dataProvider providerWriteValidEwkt * * @covers ::write */ public function testWritingValidEwkt(string $expectedWkt, Geometry $geometry, ?int $srid): void { $geometry->setSRID($srid); $wkt = (new EWKT())->write($geometry); $this->assertEquals($expectedWkt, $wkt); } /** * @return array{array{string, Geometry, int}} */ public function providerWriteValidEwkt() { return [ [ 'SRID=3857;POINT (1 2)', new Point(1, 2), 3857, ], [ 'SRID=4326;POINT Z (19.1 47.1 100)', new Point(19.1, 47.1, 100), 4326, ], [ 'POINT (1 2)', new Point(1, 2), null, ], [ 'SRID=23700;POINT EMPTY', new Point(), 23700, ], ]; } } <?php namespace geoPHP\Tests\Unit\Exception; use geoPHP\Exception\Exception; use geoPHP\Exception\InvalidGeometryException; use PHPUnit\Framework\TestCase; use RuntimeException; /** * @coversDefaultClass geoPHP\Exception\InvalidGeometryException */ class InvalidGeometryExceptionTest extends TestCase { public function testConstructWithDefaults(): void { $e = new InvalidGeometryException(); $this->assertInstanceOf(Exception::class, $e); $this->assertInstanceOf(RuntimeException::class, $e); } } <?php namespace geoPHP\Tests\Unit\Exception; use geoPHP\Exception\Exception; use geoPHP\Exception\InvalidXmlException; use geoPHP\Exception\IOException; use PHPUnit\Framework\TestCase; /** * @coversDefaultClass geoPHP\Exception\InvalidXmlException */ class InvalidXmlExceptionTest extends TestCase { public function testConstructWithDefaults(): void { $e = new InvalidXmlException(); $this->assertInstanceOf(Exception::class, $e); $this->assertInstanceOf(IOException::class, $e); $this->assertSame(0, $e->getCode()); $this->assertNull($e->getPrevious()); $this->assertSame('IO error: Invalid XML.', $e->getMessage()); } public function testConstruct(): void { $previousStub = $this->getMockForAbstractClass(Exception::class); $e = new InvalidXmlException("Error message", 1, $previousStub); $this->assertSame('IO error: Invalid XML. Error message', $e->getMessage()); } } <?php namespace geoPHP\Tests\Unit\Exception; use geoPHP\Exception\Exception; use geoPHP\Exception\IOException; use PHPUnit\Framework\TestCase; use RuntimeException; /** * @coversDefaultClass geoPHP\Exception\IOException */ class IOExceptionTest extends TestCase { public function testConstructWithDefaults(): void { $e = new IOException(); $this->assertInstanceOf(Exception::class, $e); $this->assertInstanceOf(RuntimeException::class, $e); $this->assertSame(0, $e->getCode()); $this->assertNull($e->getPrevious()); $this->assertSame('IO error: ', $e->getMessage()); } public function testConstruct(): void { $previousStub = $this->getMockForAbstractClass(Exception::class); $e = new IOException("Error message", 1, $previousStub); $this->assertSame('IO error: Error message', $e->getMessage()); } } <?php namespace geoPHP\Tests\Unit\Exception; use geoPHP\Exception\Exception; use geoPHP\Exception\FileFormatException; use geoPHP\Exception\IOException; use PHPUnit\Framework\TestCase; /** * @coversDefaultClass geoPHP\Exception\FileFormatException */ class FileFormatExceptionTest extends TestCase { public function testConstructWithDefaults(): void { $e = new FileFormatException(); $this->assertInstanceOf(Exception::class, $e); $this->assertInstanceOf(IOException::class, $e); $this->assertSame(0, $e->getCode()); $this->assertNull($e->getPrevious()); $this->assertSame('IO error: ', $e->getMessage()); } public function testConstruct(): void { $previousStub = $this->getMockForAbstractClass(Exception::class); $e = new FileFormatException("Error message", '<xml malformed>', 1, $previousStub); $this->assertSame('IO error: Error message Data: "<xml malformed>"', $e->getMessage()); } } <?php namespace geoPHP\Tests\Unit\Exception; use geoPHP\Exception\Exception; use geoPHP\Exception\UnsupportedMethodException; use LogicException; use PHPUnit\Framework\TestCase; /** * @coversDefaultClass geoPHP\Exception\UnsupportedMethodException */ class UnsupportedMethodExceptionTest extends TestCase { public function testConstructWithDefaults(): void { $e = new UnsupportedMethodException("Test"); $this->assertInstanceOf(Exception::class, $e); $this->assertInstanceOf(LogicException::class, $e); $this->assertSame(0, $e->getCode()); $this->assertNull($e->getPrevious()); $this->assertSame('Method Test() is not supported yet.', $e->getMessage()); } public function testConstruct(): void { $previousStub = $this->getMockForAbstractClass(Exception::class); $e = new UnsupportedMethodException("Test", "Error message", 1, $previousStub); $this->assertSame('Method Test() is not supported yet. Error message', $e->getMessage()); } public function testGeos(): void { $e = UnsupportedMethodException::geos("Test"); $this->assertInstanceOf(UnsupportedMethodException::class, $e); $this->assertSame('Method Test() is not supported yet. Please install GEOS extension.', $e->getMessage()); } } <?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom" xmlns:georss="http://www.georss.org/georss"> <title>Earthquakes International earthquake observation labs 2005-12-13T18:30:02Z Dr. Thaddeus Remor tremor@quakelab.edu urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 M 3.2, Mona Passage urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a 2005-08-17T07:02:32Z We just had a big one. 45.256 -71.92 GEOMETRYCOLLECTION (POINT (4 6), LINESTRING (4 6,7 10)) 1374Vista Ridge TrailheadTrail Head 1777Wy'East Basin 1823Dollar Lake 2394Barrett SpurSummit Barrett Spur 1 Barrett Spur 2 ACTIVE LOG195540 49.734 49.734 49.668 49.565 49.829 49.462 50.291 50.389 50.556 50.064 50.011 49.890 50.022 49.919 49.837 49.736 49.964 50.046 49.990 50.069 50.201 50.130 50.201 50.225 50.127 50.175 50.122 50.088 49.956 ACTIVE LOG195605 49.956 49.847 50.035 49.879 50.003 50.582 ACTIVE LOG195613 50.154 50.154 50.141 49.948 49.728 49.879 50.164 49.916 49.821 49.184 49.993 49.850 49.631 49.620 49.702 49.546 49.348 49.216 49.427 49.470 49.427 49.438 49.549 49.332 49.319 49.543 49.538 49.528 49.765 49.842 49.660 49.691 49.615 49.710 49.669 49.609 49.705 49.727 49.841 49.684 49.801 49.693 49.596 49.637 49.683 49.641 49.708 49.643 49.512 49.714 49.672 49.708 49.661 49.709 49.620 49.619 49.675 49.673 49.817 49.699 49.689 49.706 49.739 49.795 49.769 49.773 49.755 49.566 49.374 49.570 49.421 49.462 49.278 49.129 48.656 48.488 48.373 48.140 48.170 48.182 47.814 47.728 47.629 47.640 47.327 47.032 46.972 46.936 47.212 47.660 47.513 47.549 47.582 47.461 47.956 48.202 47.862 47.719 48.081 48.491 48.461 48.577 48.722 48.644 48.828 48.955 48.848 49.268 49.761 49.422 49.649 49.487 49.804 50.129 50.160 50.173 49.929 50.093 50.121 50.084 50.348 50.596 50.612 50.390 50.831 50.807 51.112 51.500 51.855 51.773 52.170 52.402 52.360 52.392 52.323 52.548 52.681 53.024 52.911 53.148 53.618 53.703 53.850 53.874 54.005 54.275 54.401 54.491 54.805 54.945 55.163 54.923 54.836 55.142 55.174 55.554 55.726 55.589 55.763 55.914 55.932 56.117 56.307 56.621 56.806 56.948 56.773 56.628 56.617 56.705 57.268 57.189 57.353 57.269 57.655 57.975 58.090 58.101 58.018 57.895 57.863 57.863 57.971 57.971 58.146 58.395 58.434 58.285 58.294 58.221 58.325 58.146 58.247 58.597 58.638 58.636 58.777 58.884 58.829 58.904 58.918 58.840 59.164 59.455 59.419 59.359 59.891 59.947 60.030 60.326 60.490 60.450 60.413 60.477 60.556 60.691 60.730 60.734 60.996 61.300 61.395 61.665 61.834 61.943 61.974 62.019 61.911 61.950 62.035 62.381 62.917 62.997 63.075 62.908 62.757 62.678 62.749 62.812 62.992 63.338 63.473 63.801 63.948 64.271 64.773 64.794 65.011 65.534 65.609 65.779 65.627 65.655 65.550 65.845 65.797 65.945 66.000 65.974 66.071 66.167 66.127 66.235 66.005 66.256 66.771 66.974 66.963 67.097 67.204 67.175 67.391 67.387 67.313 67.437 67.489 67.568 67.748 67.750 67.810 67.937 68.158 68.268 68.220 68.275 68.568 68.520 68.776 68.715 68.812 68.973 69.073 69.109 69.394 69.447 69.470 69.693 69.631 69.749 69.740 69.862 69.890 69.788 69.816 69.802 69.877 69.957 69.977 69.954 70.235 70.276 70.226 69.962 70.159 70.297 70.446 70.558 70.681 70.643 70.681 70.707 70.732 70.945 71.029 71.442 71.468 71.529 71.491 71.412 71.590 71.557 71.756 71.802 72.006 72.126 72.174 72.203 72.294 72.344 72.519 72.469 72.543 72.662 72.903 73.067 73.236 73.172 73.405 73.524 73.516 73.611 73.539 73.843 73.927 74.017 73.879 74.043 74.125 74.141 74.083 74.154 74.260 74.303 74.379 74.550 74.601 74.793 74.794 74.791 74.564 74.360 74.934 75.166 75.354 75.346 75.271 75.141 75.337 75.492 75.542 75.658 75.600 75.657 75.638 75.590 75.463 75.796 75.687 75.805 75.638 75.771 75.934 75.988 75.875 75.961 75.856 76.047 75.897 76.029 75.884 75.995 75.993 76.006 76.033 75.972 75.948 75.999 75.898 75.962 75.920 75.949 75.976 75.973 75.926 75.997 76.061 75.884 75.987 76.024 75.972 75.916 75.858 75.999 76.010 75.896 75.949 75.942 75.971 75.942 75.789 75.969 76.007 75.875 75.865 75.992 75.977 75.966 75.938 75.959 75.899 75.955 75.953 75.892 75.964 75.956 75.965 75.944 75.878 75.887 75.821 75.927 75.711 75.725 75.744 75.581 75.687 75.672 75.717 75.625 75.594 75.642 75.667 75.641 75.668 75.613 75.762 75.755 75.655 75.592 75.693 75.607 75.671 75.608 75.656 75.681 75.591 75.529 75.588 75.586 75.465 75.524 75.541 75.452 75.511 75.168 75.211 75.212 75.142 75.439 75.506 75.391 75.442 75.533 75.513 75.514 75.536 75.553 75.569 75.621 75.796 75.617 75.557 75.619 75.633 75.578 75.540 75.673 75.613 75.614 75.612 75.627 75.590 75.671 75.643 75.702 75.637 75.722 75.681 75.669 75.643 75.687 75.674 75.760 75.676 75.587 75.562 75.534 75.474 75.608 75.622 75.549 75.537 75.541 75.589 75.620 75.634 75.625 75.663 75.643 75.623 75.643 75.530 75.500 75.583 75.600 75.530 75.421 75.507 75.487 75.428 75.459 75.352 75.460 75.422 75.252 75.460 75.354 75.586 75.490 75.416 75.494 75.497 75.438 75.446 75.372 75.416 75.474 75.583 75.562 75.612 75.626 75.557 75.595 75.508 75.617 75.533 75.503 75.660 75.684 75.559 75.560 75.646 75.648 75.563 75.623 75.535 75.532 75.571 75.595 75.659 75.569 75.633 75.736 75.655 75.547 75.585 75.658 75.622 75.687 75.631 75.696 75.648 75.713 75.808 75.612 75.763 75.657 75.679 75.727 75.677 75.734 75.722 75.646 75.526 75.684 75.817 75.782 75.837 75.787 75.649 75.775 75.583 75.757 75.766 75.621 75.753 75.796 75.641 75.584 75.815 75.829 75.886 75.929 75.915 75.943 75.889 75.954 76.058 75.986 75.966 75.981 76.020 75.969 75.976 75.883 75.877 75.903 76.024 76.028 75.830 76.106 75.866 76.016 75.957 76.171 76.083 76.149 76.206 76.050 76.110 76.157 76.228 76.188 76.195 76.284 76.226 76.204 76.124 76.300 76.242 76.225 76.188 76.242 76.081 76.077 76.119 76.047 76.134 76.160 76.159 76.246 76.192 76.200 76.262 76.307 76.306 76.274 76.450 76.449 76.388 76.218 76.312 76.245 76.271 76.477 76.492 76.488 76.419 76.629 76.560 76.696 76.640 76.673 76.649 76.629 76.488 76.627 76.687 76.594 76.503 76.484 76.729 76.532 76.716 76.726 76.686 76.722 76.618 76.773 76.774 76.747 76.675 76.706 76.906 76.810 76.838 76.898 76.727 76.631 76.723 76.709 76.674 76.927 76.598 76.645 76.673 76.698 76.793 76.787 76.765 76.756 76.755 76.812 76.875 76.823 77.002 76.940 77.066 77.187 77.146 77.298 77.117 77.090 77.200 77.238 77.057 77.149 77.195 77.173 77.317 77.298 77.183 77.285 77.291 77.195 77.239 77.375 77.200 77.386 77.327 77.310 77.227 77.305 77.304 77.324 77.302 77.253 77.334 77.465 77.266 77.365 77.369 77.532 77.441 77.419 77.391 77.509 77.545 77.575 77.508 77.353 77.471 77.515 77.582 77.351 77.559 77.476 77.409 77.643 77.430 77.519 77.555 77.582 77.674 77.403 77.412 77.470 77.405 77.415 77.503 77.523 77.517 77.458 77.459 77.508 77.542 77.538 77.613 77.530 77.630 77.599 77.593 77.626 77.614 77.605 77.638 77.637 77.581 77.698 77.676 77.677 77.700 77.733 77.647 77.738 77.766 77.858 77.959 78.000 77.877 77.860 77.840 77.847 78.041 77.995 77.909 78.045 77.898 78.087 78.041 78.055 77.937 77.991 78.141 77.975 77.969 77.983 77.971 77.975 78.011 78.197 78.034 78.107 78.095 78.128 78.187 78.072 78.063 78.154 78.152 78.100 78.207 78.193 78.120 78.211 78.104 78.300 78.180 78.173 78.185 78.189 78.174 78.204 78.243 78.233 78.361 78.444 78.390 78.454 78.469 78.536 78.590 78.461 78.536 78.566 78.562 78.394 78.561 78.660 78.603 78.469 78.412 78.508 78.422 78.452 78.561 78.470 78.569 78.522 78.415 78.524 78.491 78.476 78.512 78.536 78.461 78.462 78.508 78.538 78.735 78.683 78.570 78.542 78.596 78.563 78.577 78.573 78.563 78.694 78.816 78.680 78.622 78.629 78.654 78.644 78.685 78.694 78.745 78.659 78.675 78.711 78.717 78.700 78.690 78.733 78.819 78.785 78.723 78.766 78.849 78.810 78.898 79.034 79.141 78.914 78.944 78.863 78.954 79.076 79.008 78.914 78.915 79.050 78.885 78.946 78.971 79.120 78.986 78.926 78.948 79.115 79.095 79.114 79.228 79.105 79.151 79.241 79.189 79.240 79.056 79.033 79.068 79.125 79.091 79.132 79.226 79.340 79.193 79.141 79.135 79.122 79.140 79.154 79.359 79.246 79.208 79.196 79.227 79.231 79.327 79.435 79.312 79.272 79.279 79.373 79.356 79.411 79.366 79.370 79.430 79.372 79.472 79.358 79.411 79.545 79.458 79.640 79.698 79.740 79.722 79.709 79.804 79.651 79.791 79.759 79.804 79.567 79.614 79.533 79.588 79.772 79.875 79.880 80.077 80.033 79.995 79.969 79.977 79.963 80.026 80.035 80.004 79.806 79.770 79.680 79.555 79.658 79.755 79.675 79.584 79.656 79.470 79.484 79.503 79.426 79.437 79.062 78.966 79.166 79.018 79.157 79.027 79.066 79.012 78.942 78.951 79.041 79.052 78.979 79.178 79.202 79.207 79.205 79.463 79.442 79.434 79.403 79.383 79.536 79.443 79.390 79.390 79.422 79.638 79.603 79.669 79.501 79.456 79.669 79.677 79.602 79.495 79.565 79.649 79.594 79.669 79.599 79.642 79.691 79.655 79.604 79.499 79.321 79.161 79.312 79.278 79.308 79.307 79.120 78.921 79.024 78.817 78.766 78.746 78.576 78.551 78.499 78.455 78.530 78.481 78.501 78.293 78.220 78.292 78.074 77.993 77.933 77.940 77.869 77.838 77.770 77.699 77.702 77.591 77.673 77.493 77.263 77.145 77.394 77.370 77.223 77.356 77.337 77.187 77.198 77.182 77.093 77.051 77.223 77.041 77.086 76.917 77.013 77.206 77.347 77.572 77.850 78.003 77.877 78.190 78.465 78.521 78.596 78.687 78.851 78.866 79.089 79.082 79.345 79.410 79.618 79.868 79.986 80.270 80.269 80.387 80.378 80.238 80.393 80.528 80.548 80.547 80.754 81.224 81.288 81.265 81.386 81.556 81.448 81.638 81.745 81.759 81.773 81.775 81.829 81.794 81.829 81.861 82.035 82.077 82.085 82.164 82.222 82.206 82.237 82.144 82.209 82.106 82.235 82.209 82.301 82.206 82.356 82.479 82.439 82.501 82.299 82.417 82.302 82.327 82.257 82.089 82.080 82.078 82.177 82.080 81.957 81.786 81.435 81.715 81.748 81.740 81.484 81.466 81.424 81.256 81.328 81.271 81.270 81.242 81.338 81.318 81.316 81.283 81.284 80.965 80.903 81.028 80.853 80.947 80.587 80.697 80.580 80.437 80.461 80.463 80.183 80.320 80.082 79.981 79.918 79.955 79.825 79.746 79.924 79.829 79.844 79.954 79.853 79.958 79.951 80.066 80.234 80.285 80.406 80.674 80.725 80.821 80.867 80.606 80.520 80.507 80.384 80.438 80.394 80.241 80.211 79.660 79.426 79.194 79.277 79.260 78.936 78.702 78.597 78.448 78.375 78.254 78.002 78.256 78.066 77.977 77.951 77.989 77.985 77.894 77.947 77.666 77.429 77.195 77.222 77.192 77.188 77.036 77.053 77.176 77.357 77.366 77.525 77.566 77.704 78.162 78.300 78.494 78.370 78.706 78.529 78.378 78.560 78.587 78.574 78.595 78.325 78.505 78.516 78.381 78.518 78.516 78.455 78.318 78.268 78.229 78.086 77.796 77.672 77.125 77.014 77.009 76.875 76.595 76.252 76.113 76.188 75.994 75.773 75.798 75.741 75.792 75.750 75.685 75.578 75.140 74.943 74.833 74.778 74.645 73.808 73.566 73.630 73.560 73.386 73.118 73.176 73.412 73.788 73.698 73.831 73.821 73.871 74.052 73.841 73.791 73.353 73.235 73.107 72.997 72.784 72.452 72.361 72.193 71.970 71.916 71.722 71.684 71.276 71.269 70.943 70.682 70.404 69.964 69.859 69.631 69.457 69.298 69.310 69.201 69.141 68.812 68.256 68.188 67.891 67.710 67.746 67.420 67.393 67.096 67.061 66.616 66.369 66.313 66.249 66.171 66.065 65.519 65.706 65.745 66.167 66.335 66.313 65.837 65.802 65.481 65.008 64.787 64.588 64.257 64.015 64.305 64.367 64.140 64.115 64.069 64.087 64.185 63.917 63.927 63.890 63.758 63.784 63.540 63.453 63.621 63.515 63.292 63.225 63.147 62.966 62.862 62.510 62.332 62.336 62.145 61.671 60.962 60.897 60.804 60.732 60.551 60.244 60.230 60.325 60.316 60.427 60.455 60.451 60.445 60.230 60.321 60.444 60.499 60.471 60.253 60.080 60.087 60.039 60.023 59.790 59.922 59.696 59.656 59.719 59.882 59.697 59.364 59.380 59.425 59.018 58.838 58.487 58.254 58.208 58.575 58.711 58.816 58.624 58.421 58.322 58.191 58.480 58.772 59.146 59.356 59.299 59.273 59.298 59.241 59.242 59.338 59.265 59.353 59.430 59.354 59.389 59.476 59.382 59.319 59.315 59.357 59.292 59.256 59.265 59.184 59.264 59.164 59.212 59.205 59.158 58.807 58.607 58.359 58.183 58.099 57.933 57.799 57.303 57.188 56.899 56.568 56.316 55.986 55.872 55.885 55.740 55.727 55.445 55.858 55.326 55.134 55.035 54.961 54.824 54.943 54.919 54.914 54.885 55.244 55.304 55.280 55.424 55.413 55.439 55.710 55.770 56.068 56.072 56.359 56.477 56.457 56.735 56.633 56.792 56.890 56.962 57.071 57.138 57.186 57.234 57.281 57.277 57.327 57.452 57.487 57.714 57.707 57.943 57.991 57.978 58.050 58.122 58.149 58.203 58.330 58.434 58.583 58.547 58.738 58.688 58.753 58.645 58.638 58.919 59.193 59.265 59.434 59.533 59.679 59.642 59.562 59.444 59.266 59.259 59.297 59.446 59.408 59.728 59.676 59.671 59.717 59.688 59.401 59.242 58.967 58.857 58.697 58.634 58.368 58.289 58.376 58.229 58.100 58.043 58.065 58.050 58.109 58.239 58.294 58.078 58.046 57.962 58.039 58.009 57.996 58.051 57.860 57.703 57.704 57.447 57.379 57.299 57.116 57.094 57.025 56.931 56.842 56.797 56.640 56.716 56.536 56.598 56.405 56.402 56.219 56.335 56.350 56.233 56.160 56.070 56.090 55.971 56.005 55.858 55.955 55.901 55.626 55.532 55.409 55.202 55.198 55.424 55.414 55.220 55.458 55.451 55.272 55.056 54.766 54.648 54.623 54.611 54.643 54.412 54.383 54.411 54.412 54.567 54.592 54.609 54.560 54.332 54.301 54.284 54.415 54.322 54.289 53.907 54.103 54.025 53.913 53.846 53.591 53.657 53.495 53.289 52.821 52.529 52.769 52.719 52.920 52.774 52.562 52.287 52.390 52.210 51.872 51.671 51.349 51.191 51.350 51.120 51.056 51.001 50.963 50.841 50.789 50.665 50.633 50.467 50.319 50.070 50.030 50.128 50.189 49.975 49.856 49.607 49.733 49.561 49.352 49.277 49.290 49.041 48.932 48.730 48.730 48.710 48.767 48.976 48.619 48.381 48.349 48.285 48.107 47.954 47.686 47.628 47.878 47.872 47.856 47.626 47.425 47.337 47.281 47.273 47.365 47.150 46.898 46.873 46.703 46.652 46.558 46.430 46.355 46.307 46.211 46.097 46.141 46.111 46.182 46.044 46.079 46.051 45.911 45.775 45.766 45.750 45.834 45.994 46.037 46.013 45.989 46.065 46.247 46.293 46.437 46.483 46.588 46.882 47.155 47.080 46.973 46.805 46.864 46.662 46.727 46.843 46.632 46.671 46.526 46.667 46.666 46.561 46.710 46.616 46.688 46.722 46.753 46.482 46.574 46.574 46.377 46.361 46.178 46.059 46.106 45.955 45.752 45.682 45.445 45.375 45.509 45.468 45.461 45.354 45.606 45.374 45.282 45.341 45.717 45.638 45.593 45.427 45.316 45.131 44.901 44.754 44.685 44.538 44.364 44.145 44.155 44.103 44.005 43.784 43.596 43.522 43.453 43.250 43.194 43.193 43.098 43.044 42.712 42.615 42.333 42.223 42.182 42.204 42.003 41.782 41.763 41.704 42.079 42.092 41.830 41.864 41.848 41.420 41.444 41.401 41.136 40.874 40.989 40.975 40.860 40.568 40.390 40.253 40.318 40.337 40.106 40.049 39.971 39.795 39.868 40.322 40.496 40.363 40.373 40.332 40.659 40.850 40.798 40.949 40.680 40.709 40.675 40.450 40.470 40.314 40.274 40.218 39.967 39.922 39.965 39.844 39.937 39.942 39.877 39.867 39.868 39.808 39.845 39.830 39.920 39.962 39.833 39.860 39.830 39.664 39.793 39.804 39.812 39.872 39.980 39.937 39.931 40.018 39.959 39.971 39.862 39.940 40.027 39.829 39.863 39.820 39.683 39.580 39.376 39.284 39.361 39.276 38.949 38.815 38.738 38.614 38.461 38.298 38.229 37.942 37.886 37.868 37.702 37.694 37.573 37.608 37.429 37.420 37.408 37.428 37.472 37.252 37.246 37.063 36.895 36.681 36.614 36.658 36.757 36.702 36.706 36.678 36.842 37.039 37.296 37.165 37.260 37.327 37.264 37.204 37.135 36.948 37.070 37.047 36.926 36.874 36.768 36.661 36.529 36.343 36.335 36.392 36.133 36.054 35.962 35.740 35.655 35.721 35.782 35.727 35.645 35.574 35.627 35.607 35.630 35.607 35.614 35.660 35.651 35.741 35.840 35.627 35.699 35.871 35.667 35.697 35.795 35.774 35.980 35.891 36.018 36.265 36.347 36.458 36.422 36.488 36.168 35.982 36.064 35.862 35.885 35.868 35.820 35.805 35.744 35.816 36.069 36.308 36.247 36.050 36.287 36.146 36.129 35.753 35.863 35.686 36.203 35.978 35.847 35.981 35.931 35.809 35.774 35.848 35.953 35.858 35.908 35.986 35.988 36.024 36.161 36.005 35.986 36.184 36.207 36.122 36.188 36.248 36.242 36.163 36.049 36.255 36.315 36.241 36.039 36.115 36.083 36.253 36.374 36.511 36.611 36.578 36.561 36.685 36.692 36.681 36.701 36.685 36.716 36.713 36.730 36.693 36.700 36.631 36.530 36.487 36.476 36.401 36.393 36.437 36.375 36.416 36.426 36.412 36.324 36.192 36.223 36.441 35.991 36.000 35.965 35.885 36.066 36.131 36.074 36.088 36.245 36.167 36.324 36.253 36.246 35.887 36.036 36.105 36.070 36.115 36.038 36.088 36.178 36.189 36.123 36.067 36.150 36.238 36.307 36.195 36.291 36.205 36.322 36.172 36.205 36.195 36.293 36.357 36.222 36.193 36.167 36.185 36.243 36.112 36.093 36.200 36.236 36.325 36.311 36.320 36.335 36.158 36.280 36.090 36.350 36.265 36.183 36.172 36.066 36.024 36.019 36.133 36.084 36.037 36.165 36.062 36.243 36.238 36.165 36.072 35.982 36.019 35.905 36.065 36.045 35.969 35.811 35.661 35.558 35.501 35.396 35.421 35.475 35.497 35.476 35.376 35.395 35.544 35.431 35.456 35.472 35.597 35.495 35.501 35.415 35.477 35.406 35.296 35.302 35.298 35.236 35.258 35.608 35.637 35.707 35.957 35.879 36.071 36.044 35.890 35.995 35.985 35.942 35.974 36.063 35.981 36.089 36.007 35.869 35.991 35.804 35.696 35.479 35.503 35.303 34.981 35.030 34.634 34.662 34.695 34.855 34.947 34.686 34.614 34.830 34.797 34.455 33.928 33.990 34.044 34.071 33.802 33.610 33.373 33.558 33.622 33.246 32.969 33.212 33.259 33.414 33.591 33.543 33.332 33.746 33.757 33.873 33.807 33.633 33.707 33.802 33.911 33.695 33.819 33.819 33.798 33.798 33.769 33.783 33.851 33.622 33.893 33.720 33.804 34.214 33.885 33.807 33.713 33.764 33.869 33.844 33.916 33.825 33.972 33.870 33.985 33.889 33.711 33.958 33.965 33.776 33.830 34.064 33.836 33.676 33.785 33.575 33.360 33.274 33.355 33.687 33.445 33.116 33.054 33.098 33.224 33.427 33.464 33.154 33.199 32.717 32.626 32.535 32.543 32.314 32.121 32.074 32.074 31.913 31.857 31.799 31.876 31.609 31.546 31.520 31.588 31.633 31.373 31.406 31.176 31.066 30.925 31.016 30.719 30.508 30.319 30.207 30.321 29.985 29.980 29.773 29.868 29.828 30.055 29.769 29.904 30.128 29.898 29.530 29.563 29.493 29.540 29.547 29.642 29.741 29.704 29.594 29.447 29.260 29.740 29.403 29.482 29.426 29.357 29.293 29.409 29.488 29.543 29.781 30.203 30.303 30.556 30.907 31.083 31.212 31.412 31.473 31.697 31.795 31.837 31.802 31.937 31.764 31.381 31.284 31.013 30.375 29.964 29.182 28.814 28.476 28.389 28.632 28.786 28.871 29.141 29.394 29.567 29.485 29.660 29.962 29.944 30.077 30.065 30.076 30.119 30.289 30.448 30.498 30.575 30.596 30.635 30.656 30.627 30.866 31.021 31.162 31.232 31.299 31.614 31.725 31.942 32.027 32.264 32.108 32.346 32.443 32.588 32.743 32.843 32.982 32.953 33.063 33.256 33.433 33.729 33.800 34.126 34.142 34.231 34.425 34.652 34.780 34.739 35.134 35.302 35.324 35.505 35.754 35.935 35.910 35.990 36.226 36.266 36.452 36.514 36.729 36.869 36.958 37.230 37.322 37.398 37.574 37.764 37.824 37.984 38.198 38.345 38.394 38.473 38.560 38.771 38.995 39.132 39.200 39.336 39.444 39.635 39.863 39.937 39.848 40.056 40.262 40.399 40.438 40.448 40.625 40.783 40.748 40.874 41.090 41.182 41.269 41.380 41.406 41.463 41.489 41.756 41.850 42.141 42.401 42.417 42.524 42.650 42.903 43.016 42.913 43.041 43.218 43.434 43.661 43.821 43.687 43.808 43.849 43.784 43.654 43.830 43.961 43.834 43.989 44.058 44.062 44.029 44.015 44.024 44.061 44.147 44.242 44.144 44.170 44.146 44.186 44.202 44.134 44.134 44.140 44.013 43.652 43.784 43.531 43.537 43.446 43.317 43.062 42.976 42.950 42.975 42.278 42.218 42.205 42.145 41.932 41.985 41.692 41.463 41.289 41.187 41.122 40.973 40.861 40.815 40.861 40.826 40.245 40.022 39.513 39.390 38.550 38.290 37.876 37.764 37.305 36.971 36.700 36.392 36.304 35.896 35.595 35.036 34.585 34.293 33.940 33.087 32.805 32.357 32.243 32.223 32.564 32.037 32.057 32.196 31.767 31.544 31.574 31.639 31.652 31.762 31.796 31.724 31.012 31.029 31.064 31.551 31.551 31.438 31.644 31.887 31.964 32.203 32.119 31.948 31.995 31.874 32.074 32.155 32.289 32.512 32.564 32.663 32.715 32.724 32.878 32.961 33.092 33.341 33.426 33.471 33.777 34.089 34.522 34.656 34.804 35.141 35.262 35.700 36.011 36.045 36.576 36.685 36.888 37.221 37.827 37.920 38.044 38.438 38.595 38.746 38.522 38.676 39.020 39.126 39.222 39.493 39.858 39.935 40.095 40.251 40.461 40.707 40.789 41.059 41.265 41.629 41.920 41.874 42.289 42.392 42.574 42.878 42.955 43.265 43.449 43.703 43.853 43.864 44.067 44.203 44.306 44.504 44.431 44.564 44.837 45.295 45.262 45.400 45.460 45.651 46.054 46.018 46.187 46.484 46.632 46.765 46.901 47.134 47.228 47.411 47.665 47.706 47.875 47.966 48.037 48.170 48.184 48.446 48.536 48.796 48.891 48.928 48.965 49.116 49.196 49.236 49.537 49.392 49.417 49.629 49.564 49.853 49.983 50.066 50.275 50.247 50.314 50.437 50.361 50.803 50.693 50.921 51.133 51.044 51.065 51.203 51.148 51.268 51.226 51.230 51.427 51.359 51.522 51.380 51.328 51.317 51.300 51.261 51.300 51.272 51.086 51.133 51.162 51.082 51.012 51.159 50.768 50.835 50.673 50.649 50.815 50.655 50.448 50.394 50.449 50.387 50.468 50.451 50.598 50.511 50.573 50.523 50.584 50.568 50.636 50.795 50.586 50.831 50.868 50.763 50.802 50.749 50.717 50.804 50.762 50.645 50.709 50.611 50.653 50.581 50.732 50.769 50.076 49.831 MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10)) LINESTRING (30 10, 10 30, 40 40) Simple placemark Attached to the ground. Intelligently places itself at the height of the underlying terrain. -122.0822035425683,37.42228990140251,0 POLYGON ((-123.222653196 49.1529676585, -89.4726531957 49.3823707987, -81.0351531957 44.0875828344, -71.1914031957 44.3395630636, -62.0507781957 48.4583498573, -60.2929656957 45.0890334085, -78.9257781957 37.4399716272, -82.0898406957 31.3536343332, -81.3867156957 26.4312253295, -91.9335906957 29.8406412505, -98.2617156957 26.4312253295, -107.753903196 32.2499718728, -116.894528196 33.1375486348, -122.519528196 36.0313293064, -126.035153196 42.2935619329, -123.222653196 49.1529676585)) KML Samples 1 Unleash your creativity with the help of these examples! Placemarks These are just some of the different kinds of placemarks with which you can mark your favorite places -122.0839597145766 37.42222904525232 0 -148.4122922628044 40.5575073395506 500.6566641072245 Simple placemark Attached to the ground. Intelligently places itself at the height of the underlying terrain. -122.0822035425683,37.42228990140251,0 Floating placemark 0 Floats a defined distance above the ground. -122.0839597145766 37.42222904525232 0 -148.4122922628044 40.5575073395506 500.6566641072245 #downArrowIcon relativeToGround -122.084075,37.4220033612141,50 Extruded placemark 0 Tethered to the ground by a customizable "tail" -122.0845787421525 37.42215078737763 0 -148.4126684946234 40.55750733918048 365.2646606980322 #globeIcon 1 relativeToGround -122.0857667006183,37.42156927867553,50 Styles and Markup 0 With KML it is easy to create rich, descriptive markup to annotate and enrich your placemarks -122.0845787422371 37.42215078726837 0 -148.4126777488172 40.55750733930874 365.2646826292919 #noDrivingDirections Highlighted Icon 0 Place your mouse over the icon to see it display the new icon -122.0856552124024 37.4224281311035 0 0 0 265.8520424250024 normal #normalPlacemark highlight #highlightPlacemark Roll over this icon 0 #exampleStyleMap -122.0856545755255,37.42243077405461,0 Descriptive HTML 0
Placemark descriptions can be enriched by using many standard HTML tags.
For example:
Styles:
Italics, Bold, Underlined, Strike Out, subscriptsubscript, superscriptsuperscript, Big, Small, Typewriter, Emphasized, Strong, Code
Fonts:
red by name, leaf green by hexadecimal RGB
size 1, size 2, size 3, size 4, size 5, size 6, size 7
Times, Verdana, Arial

Links:
Google Earth!
or: Check out our website at www.google.com
Alignment:

left

center

right


Ordered Lists:
  1. First
  2. Second
  3. Third
  1. First
  2. Second
  3. Third
  1. First
  2. Second
  3. Third

Unordered Lists:
  • A
  • B
  • C
  • A
  • B
  • C
  • A
  • B
  • C

Definitions:
Google:
The best thing since sliced bread

Centered:
Time present and time past
Are both perhaps present in time future,
And time future contained in time past.
If all time is eternally present
All time is unredeemable.

Block Quote:
We shall not cease from exploration
And the end of all our exploring
Will be to arrive where we started
And know the place for the first time.
-- T.S. Eliot


Headings:

Header 1

Header 2

Header 3

Header 4

Header 5


Images:
Remote image

Scaled image


Simple Tables:
12345
abcde

[Did you notice that double-clicking on the placemark doesn't cause the viewer to take you anywhere? This is because it is possible to directly author a "placeless placemark". If you look at the code for this example, you will see that it has neither a point coordinate nor a LookAt element.]]]>
Ground Overlays 0 Examples of ground overlays Large-scale overlay on terrain 0 Overlay shows Mount Etna erupting on July 13th, 2001. 15.02468937557116 37.67395167941667 0 -16.5581842842829 58.31228652890705 30350.36838438907 http://code.google.com/apis/kml/documentation/etna.jpg 37.91904192681665 37.46543388598137 15.35832653742206 14.60128369746704 -0.1556640799496235 Screen Overlays 0 Screen overlays have to be authored directly in KML. These examples illustrate absolute and dynamic positioning in screen space. Simple crosshairs 0 This screen overlay uses fractional positioning to put the image in the exact center of the screen http://code.google.com/apis/kml/documentation/crosshairs.png Absolute Positioning: Top left 0 http://code.google.com/apis/kml/documentation/top_left.jpg Absolute Positioning: Top right 0 http://code.google.com/apis/kml/documentation/top_right.jpg Absolute Positioning: Bottom left 0 http://code.google.com/apis/kml/documentation/bottom_left.jpg Absolute Positioning: Bottom right 0 http://code.google.com/apis/kml/documentation/bottom_right.jpg Dynamic Positioning: Top of screen 0 http://code.google.com/apis/kml/documentation/dynamic_screenoverlay.jpg Dynamic Positioning: Right of screen 0 http://code.google.com/apis/kml/documentation/dynamic_right.jpg Paths 0 Examples of paths. Note that the tessellate tag is by default set to 0. If you want to create tessellated lines, they must be authored (or edited) directly in KML. Tessellated 0 tag has a value of 1, the line will contour to the underlying terrain]]> -112.0822680013139 36.09825589333556 0 103.8120432044965 62.04855796276328 2889.145007690472 1 -112.0814237830345,36.10677870477137,0 -112.0870267752693,36.0905099328766,0 Untessellated 0 tag has a value of 0, the line follow a simple straight-line path from point to point]]> -112.0822680013139 36.09825589333556 0 103.8120432044965 62.04855796276328 2889.145007690472 0 -112.080622229595,36.10673460007995,0 -112.085242575315,36.09049598612422,0 Absolute 0 Transparent purple line -112.2719329043177 36.08890633450894 0 -106.8161545998597 44.60763714063257 2569.386744398339 #transPurpleLineGreenPoly 1 absolute -112.265654928602,36.09447672602546,2357 -112.2660384528238,36.09342608838671,2357 -112.2668139013453,36.09251058776881,2357 -112.2677826834445,36.09189827357996,2357 -112.2688557510952,36.0913137941187,2357 -112.2694810717219,36.0903677207521,2357 -112.2695268555611,36.08932171487285,2357 -112.2690144567276,36.08850916060472,2357 -112.2681528815339,36.08753813597956,2357 -112.2670588176031,36.08682685262568,2357 -112.2657374587321,36.08646312301303,2357 Absolute Extruded 0 Transparent green wall with yellow outlines -112.2643334742529 36.08563154742419 0 -125.7518698668815 44.61038665812578 4451.842204068102 #yellowLineGreenPoly 1 1 absolute -112.2550785337791,36.07954952145647,2357 -112.2549277039738,36.08117083492122,2357 -112.2552505069063,36.08260761307279,2357 -112.2564540158376,36.08395660588506,2357 -112.2580238976449,36.08511401044813,2357 -112.2595218489022,36.08584355239394,2357 -112.2608216347552,36.08612634548589,2357 -112.262073428656,36.08626019085147,2357 -112.2633204928495,36.08621519860091,2357 -112.2644963846444,36.08627897945274,2357 -112.2656969554589,36.08649599090644,2357 Relative 0 Black line (10 pixels wide), height tracks terrain -112.2580438551384 36.1072674824385 0 4.947421249553717 44.61324882043339 2927.61105910266 #thickBlackLine 1 relativeToGround -112.2532845153347,36.09886943729116,645 -112.2540466121145,36.09919570465255,645 -112.254734666947,36.09984998366178,645 -112.255493345654,36.10051310621746,645 -112.2563157098468,36.10108441943419,645 -112.2568033076439,36.10159722088088,645 -112.257494011321,36.10204323542867,645 -112.2584106072308,36.10229131995655,645 -112.2596588987972,36.10240001286358,645 -112.2610581199487,36.10213176873407,645 -112.2626285262793,36.10157011437219,645 Relative Extruded 0 Opaque blue walls with red outline, height tracks terrain -112.2683594333433 36.09884362144909 0 -72.24271551768405 44.60855445139561 2184.193522571467 #redLineBluePoly 1 1 relativeToGround -112.2656634181359,36.09445214722695,630 -112.2652238941097,36.09520916122063,630 -112.2645079986395,36.09580763864907,630 -112.2638827428817,36.09628572284063,630 -112.2635746835406,36.09679275951239,630 -112.2635711822407,36.09740038871899,630 -112.2640296531825,36.09804913435539,630 -112.264327720538,36.09880337400301,630 -112.2642436562271,36.09963644790288,630 -112.2639148687042,36.10055381117246,630 -112.2626894973474,36.10149062823369,630 Polygons 0 Examples of polygon shapes Google Campus 0 A collection showing how easy it is to create 3-dimensional buildings -122.084120030116 37.42174011925477 0 -34.82469740081282 53.454348562403 276.7870053764046 Building 40 0 #transRedPoly 1 relativeToGround -122.0848938459612,37.42257124044786,17 -122.0849580979198,37.42211922626856,17 -122.0847469573047,37.42207183952619,17 -122.0845725380962,37.42209006729676,17 -122.0845954886723,37.42215932700895,17 -122.0838521118269,37.42227278564371,17 -122.083792243335,37.42203539112084,17 -122.0835076656616,37.42209006957106,17 -122.0834709464152,37.42200987395161,17 -122.0831221085748,37.4221046494946,17 -122.0829247374572,37.42226503990386,17 -122.0829339169385,37.42231242843094,17 -122.0833837359737,37.42225046087618,17 -122.0833607854248,37.42234159228745,17 -122.0834204551642,37.42237075460644,17 -122.083659133885,37.42251292011001,17 -122.0839758438952,37.42265873093781,17 -122.0842374743331,37.42265143972521,17 -122.0845036949503,37.4226514386435,17 -122.0848020460801,37.42261133916315,17 -122.0847882750515,37.42256395055121,17 -122.0848938459612,37.42257124044786,17 Building 41 0 #transBluePoly 1 relativeToGround -122.0857412771483,37.42227033155257,17 -122.0858169768481,37.42231408832346,17 -122.085852582875,37.42230337469744,17 -122.0858799945639,37.42225686138789,17 -122.0858860101409,37.4222311076138,17 -122.0858069157288,37.42220250173855,17 -122.0858379542653,37.42214027058678,17 -122.0856732640519,37.42208690214408,17 -122.0856022926407,37.42214885429042,17 -122.0855902778436,37.422128290487,17 -122.0855841672237,37.42208171967246,17 -122.0854852065741,37.42210455874995,17 -122.0855067264352,37.42214267949824,17 -122.0854430712915,37.42212783846172,17 -122.0850990714904,37.42251282407603,17 -122.0856769818632,37.42281815323651,17 -122.0860162273783,37.42244918858722,17 -122.0857260327004,37.42229239604253,17 -122.0857412771483,37.42227033155257,17 Building 42 0 #transGreenPoly 1 relativeToGround -122.0857862287242,37.42136208886969,25 -122.0857312990603,37.42136935989481,25 -122.0857312992918,37.42140934910903,25 -122.0856077073679,37.42138390166565,25 -122.0855802426516,37.42137299550869,25 -122.0852186221971,37.42137299504316,25 -122.0852277765639,37.42161656508265,25 -122.0852598189347,37.42160565894403,25 -122.0852598185499,37.42168200156,25 -122.0852369311478,37.42170017860346,25 -122.0852643957828,37.42176197982575,25 -122.0853239032746,37.42176198013907,25 -122.0853559454324,37.421852864452,25 -122.0854108752463,37.42188921823734,25 -122.0854795379357,37.42189285337048,25 -122.0855436229819,37.42188921797546,25 -122.0856260178042,37.42186013499926,25 -122.085937287963,37.42186013453605,25 -122.0859428718666,37.42160898590042,25 -122.0859655469861,37.42157992759144,25 -122.0858640462341,37.42147115002957,25 -122.0858548911215,37.42140571326184,25 -122.0858091162768,37.4214057134039,25 -122.0857862287242,37.42136208886969,25 Building 43 0 #transYellowPoly 1 relativeToGround -122.0844371128284,37.42177253003091,19 -122.0845118855746,37.42191111542896,19 -122.0850470999805,37.42178755121535,19 -122.0850719913391,37.42143663023161,19 -122.084916406232,37.42137237822116,19 -122.0842193868167,37.42137237801626,19 -122.08421938659,37.42147617161496,19 -122.0838086419991,37.4214613409357,19 -122.0837899728564,37.42131306410796,19 -122.0832796534698,37.42129328840593,19 -122.0832609819207,37.42139213944298,19 -122.0829373621737,37.42137236399876,19 -122.0829062425667,37.42151569778871,19 -122.0828502269665,37.42176282576465,19 -122.0829435788635,37.42176776969635,19 -122.083217411188,37.42179248552686,19 -122.0835970430103,37.4217480074456,19 -122.0839455556771,37.42169364237603,19 -122.0840077894637,37.42176283815853,19 -122.084113587521,37.42174801104392,19 -122.0840762473784,37.42171341292375,19 -122.0841447047739,37.42167881534569,19 -122.084144704223,37.42181720660197,19 -122.0842503333074,37.4218170700446,19 -122.0844371128284,37.42177253003091,19 Extruded Polygon A simple way to model a building The Pentagon -77.05580139178142 38.870832443487 59.88865561738225 48.09646074797388 742.0552506670548 1 relativeToGround -77.05788457660967,38.87253259892824,100 -77.05465973756702,38.87291016281703,100 -77.05315536854791,38.87053267794386,100 -77.05552622493516,38.868757801256,100 -77.05844056290393,38.86996206506943,100 -77.05788457660967,38.87253259892824,100 -77.05668055019126,38.87154239798456,100 -77.05542625960818,38.87167890344077,100 -77.05485125901024,38.87076535397792,100 -77.05577677433152,38.87008686581446,100 -77.05691162017543,38.87054446963351,100 -77.05668055019126,38.87154239798456,100 Absolute and Relative 0 Four structures whose roofs meet exactly. Turn on/off terrain to see the difference between relative and absolute positioning. -112.3348969157552 36.14845533214919 0 -86.91235037566909 49.30695423894192 990.6761201087104 Absolute 0 #transBluePoly 1 absolute -112.3382510731295,36.14888505105318,1784 -112.3356128688403,36.14781540589019,1784 -112.3368169371048,36.14658677734382,1784 -112.3374408457543,36.14762778914076,1784 -112.3382510731295,36.14888505105318,1784 Absolute Extruded 0 #transRedPoly 1 1 absolute -112.3396586818843,36.14637618647505,1784 -112.3380597654315,36.14531751871353,1784 -112.3368254237788,36.14659596244607,1784 -112.3384555043203,36.14762621763982,1784 -112.3396586818843,36.14637618647505,1784 Relative 0 -112.3350152490417 36.14943123077423 0 -118.9214100848499 37.92486261093203 345.5169113679813 #transGreenPoly 1 relativeToGround -112.3349463145932,36.14988705767721,100 -112.3354019540677,36.14941108398372,100 -112.3344428289146,36.14878490381308,100 -112.3331289492913,36.14780840132443,100 -112.3317019516947,36.14680755678357,100 -112.331131440106,36.1474173426228,100 -112.332616324338,36.14845453364654,100 -112.3339876620524,36.14926570522069,100 -112.3349463145932,36.14988705767721,100 Relative Extruded 0 -112.3351587892382 36.14979247129029 0 -55.42811560891606 56.10280503739589 401.0997279712519 #transYellowPoly 1 1 relativeToGround -112.3348783983763,36.1514008468736,100 -112.3372535345629,36.14888517553886,100 -112.3356068927954,36.14781612679284,100 -112.3350034807972,36.14846469024177,100 -112.3358353861232,36.1489624162954,100 -112.3345888301373,36.15026229372507,100 -112.3337937856278,36.14978096026463,100 -112.3331798208424,36.1504472788618,100 -112.3348783983763,36.1514008468736,100
{"type": "FeatureCollection","crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },"features": [ { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 3.000000, "type": "Sovereign country", "admin": "Afghanistan", "adm0_a3": "AFG", "name": "Afghanistan", "name_long": "Afghanistan", "abbrev": "Afg.", "postal": "AF", "formal_en": "Islamic State of Afghanistan", "name_sort": "Afghanistan", "name_alt": null, "mapcolor7": 5.000000, "mapcolor8": 6.000000, "mapcolor9": 8.000000, "mapcolor13": 7.000000, "pop_est": 28400000.000000, "gdp_md_est": 22270.000000, "pop_year": -99.000000, "lastcensus": 1979.000000, "gdp_year": -99.000000, "economy": "7. Least developed region", "income_grp": "5. Low income", "iso_a2": "AF", "iso_a3": "AFG", "iso_n3": "004", "un_a3": "004", "wb_a2": "AF", "wb_a3": "AFG", "woe_id": -99.000000, "adm0_a3_is": "AFG", "adm0_a3_us": "AFG", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "Asia", "subregion": "Southern Asia", "region_wb": "South Asia", "name_len": 11.000000, "long_len": 11.000000, "abbrev_len": 4.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 61.2108, 35.6501 ], [ 62.2307, 35.2707 ], [ 62.9847, 35.404 ], [ 63.1935, 35.8572 ], [ 63.9829, 36.008 ], [ 64.5465, 36.3121 ], [ 64.7461, 37.1118 ], [ 65.5889, 37.3052 ], [ 65.7456, 37.6612 ], [ 66.2174, 37.3938 ], [ 66.5186, 37.3628 ], [ 67.0758, 37.3561 ], [ 67.83, 37.145 ], [ 68.1356, 37.0231 ], [ 68.8594, 37.3443 ], [ 69.1963, 37.1511 ], [ 69.5188, 37.609 ], [ 70.1166, 37.5882 ], [ 70.2706, 37.7352 ], [ 70.3763, 38.1384 ], [ 70.8068, 38.4863 ], [ 71.3481, 38.2589 ], [ 71.2394, 37.9533 ], [ 71.5419, 37.9058 ], [ 71.4487, 37.0656 ], [ 71.8446, 36.7382 ], [ 72.193, 36.9483 ], [ 72.6369, 37.0476 ], [ 73.2601, 37.4953 ], [ 73.9487, 37.4216 ], [ 74.98, 37.42 ], [ 75.158, 37.133 ], [ 74.5759, 37.0208 ], [ 74.0676, 36.8362 ], [ 72.92, 36.72 ], [ 71.8463, 36.5099 ], [ 71.2623, 36.0744 ], [ 71.4988, 35.6506 ], [ 71.6131, 35.1532 ], [ 71.115, 34.7331 ], [ 71.1568, 34.3489 ], [ 70.8818, 33.9889 ], [ 69.9305, 34.0201 ], [ 70.3236, 33.3585 ], [ 69.6871, 33.1055 ], [ 69.2625, 32.5019 ], [ 69.3178, 31.9014 ], [ 68.9267, 31.6202 ], [ 68.5569, 31.7133 ], [ 67.7927, 31.5829 ], [ 67.6834, 31.3032 ], [ 66.9389, 31.3049 ], [ 66.3815, 30.7389 ], [ 66.3465, 29.8879 ], [ 65.0469, 29.4722 ], [ 64.3504, 29.56 ], [ 64.148, 29.3408 ], [ 63.5503, 29.4683 ], [ 62.5499, 29.3186 ], [ 60.8742, 29.8292 ], [ 61.7812, 30.7359 ], [ 61.6993, 31.3795 ], [ 60.9419, 31.5481 ], [ 60.8637, 32.1829 ], [ 60.5361, 32.9813 ], [ 60.9637, 33.5288 ], [ 60.5284, 33.6764 ], [ 60.8032, 34.4041 ], [ 61.2108, 35.6501 ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 3.000000, "type": "Sovereign country", "admin": "Angola", "adm0_a3": "AGO", "name": "Angola", "name_long": "Angola", "abbrev": "Ang.", "postal": "AO", "formal_en": "People's Republic of Angola", "name_sort": "Angola", "name_alt": null, "mapcolor7": 3.000000, "mapcolor8": 2.000000, "mapcolor9": 6.000000, "mapcolor13": 1.000000, "pop_est": 12799293.000000, "gdp_md_est": 110300.000000, "pop_year": -99.000000, "lastcensus": 1970.000000, "gdp_year": -99.000000, "economy": "7. Least developed region", "income_grp": "3. Upper middle income", "iso_a2": "AO", "iso_a3": "AGO", "iso_n3": "024", "un_a3": "024", "wb_a2": "AO", "wb_a3": "AGO", "woe_id": -99.000000, "adm0_a3_is": "AGO", "adm0_a3_us": "AGO", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "Africa", "subregion": "Middle Africa", "region_wb": "Sub-Saharan Africa", "name_len": 6.000000, "long_len": 6.000000, "abbrev_len": 4.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 16.3265, -5.8775 ], [ 16.5732, -6.6226 ], [ 16.8602, -7.2223 ], [ 17.09, -7.5457 ], [ 17.473, -8.0686 ], [ 18.1342, -7.9877 ], [ 18.4642, -7.847 ], [ 19.0168, -7.9882 ], [ 19.1666, -7.7382 ], [ 19.4175, -7.1554 ], [ 20.0377, -7.1164 ], [ 20.0916, -6.9431 ], [ 20.6018, -6.9393 ], [ 20.5147, -7.2996 ], [ 21.7281, -7.2909 ], [ 21.7465, -7.9201 ], [ 21.9491, -8.3059 ], [ 21.8018, -8.9087 ], [ 21.8752, -9.5237 ], [ 22.2088, -9.8948 ], [ 22.1553, -11.0848 ], [ 22.4028, -10.9931 ], [ 22.8373, -11.0176 ], [ 23.4568, -10.8679 ], [ 23.9122, -10.9268 ], [ 24.0179, -11.2373 ], [ 23.9042, -11.7223 ], [ 24.0799, -12.1913 ], [ 23.9309, -12.5658 ], [ 24.0161, -12.911 ], [ 21.9339, -12.8984 ], [ 21.8878, -16.0803 ], [ 22.5625, -16.8985 ], [ 23.215, -17.5231 ], [ 21.3772, -17.9306 ], [ 18.9562, -17.7891 ], [ 18.2633, -17.31 ], [ 14.2097, -17.3531 ], [ 14.0585, -17.4234 ], [ 13.4624, -16.9712 ], [ 12.8141, -16.9413 ], [ 12.2155, -17.1117 ], [ 11.7342, -17.3019 ], [ 11.6401, -16.6731 ], [ 11.7785, -15.7938 ], [ 12.1236, -14.8783 ], [ 12.1756, -14.4491 ], [ 12.5001, -13.5477 ], [ 12.7385, -13.1379 ], [ 13.3129, -12.4836 ], [ 13.6337, -12.0386 ], [ 13.7387, -11.2979 ], [ 13.6864, -10.7311 ], [ 13.3873, -10.3736 ], [ 13.121, -9.7669 ], [ 12.8754, -9.1669 ], [ 12.9291, -8.9591 ], [ 13.2364, -8.5626 ], [ 12.933, -7.5965 ], [ 12.7283, -6.9271 ], [ 12.2273, -6.2944 ], [ 12.3224, -6.1001 ], [ 12.7352, -5.9657 ], [ 13.0249, -5.9844 ], [ 13.3756, -5.8642 ], [ 16.3265, -5.8775 ] ] ], [ [ [ 12.4367, -5.6843 ], [ 12.1823, -5.7899 ], [ 11.915, -5.038 ], [ 12.3186, -4.6062 ], [ 12.6208, -4.438 ], [ 12.9955, -4.7811 ], [ 12.6316, -4.9913 ], [ 12.468, -5.2484 ], [ 12.4367, -5.6843 ] ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 6.000000, "type": "Sovereign country", "admin": "Albania", "adm0_a3": "ALB", "name": "Albania", "name_long": "Albania", "abbrev": "Alb.", "postal": "AL", "formal_en": "Republic of Albania", "name_sort": "Albania", "name_alt": null, "mapcolor7": 1.000000, "mapcolor8": 4.000000, "mapcolor9": 1.000000, "mapcolor13": 6.000000, "pop_est": 3639453.000000, "gdp_md_est": 21810.000000, "pop_year": -99.000000, "lastcensus": 2001.000000, "gdp_year": -99.000000, "economy": "6. Developing region", "income_grp": "4. Lower middle income", "iso_a2": "AL", "iso_a3": "ALB", "iso_n3": "008", "un_a3": "008", "wb_a2": "AL", "wb_a3": "ALB", "woe_id": -99.000000, "adm0_a3_is": "ALB", "adm0_a3_us": "ALB", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "Europe", "subregion": "Southern Europe", "region_wb": "Europe & Central Asia", "name_len": 7.000000, "long_len": 7.000000, "abbrev_len": 4.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 20.5902, 41.8554 ], [ 20.4632, 41.5151 ], [ 20.6052, 41.0862 ], [ 21.02, 40.8427 ], [ 21.0, 40.58 ], [ 20.675, 40.435 ], [ 20.615, 40.11 ], [ 20.15, 39.625 ], [ 19.98, 39.695 ], [ 19.96, 39.915 ], [ 19.4061, 40.2508 ], [ 19.3191, 40.7272 ], [ 19.4035, 41.4096 ], [ 19.54, 41.72 ], [ 19.3718, 41.8775 ], [ 19.3045, 42.1957 ], [ 19.7381, 42.6882 ], [ 19.8016, 42.5001 ], [ 20.0707, 42.5886 ], [ 20.2838, 42.3203 ], [ 20.523, 42.2179 ], [ 20.5902, 41.8554 ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 4.000000, "type": "Sovereign country", "admin": "United Arab Emirates", "adm0_a3": "ARE", "name": "United Arab Emirates", "name_long": "United Arab Emirates", "abbrev": "U.A.E.", "postal": "AE", "formal_en": "United Arab Emirates", "name_sort": "United Arab Emirates", "name_alt": null, "mapcolor7": 2.000000, "mapcolor8": 1.000000, "mapcolor9": 3.000000, "mapcolor13": 3.000000, "pop_est": 4798491.000000, "gdp_md_est": 184300.000000, "pop_year": -99.000000, "lastcensus": 2010.000000, "gdp_year": -99.000000, "economy": "6. Developing region", "income_grp": "2. High income: nonOECD", "iso_a2": "AE", "iso_a3": "ARE", "iso_n3": "784", "un_a3": "784", "wb_a2": "AE", "wb_a3": "ARE", "woe_id": -99.000000, "adm0_a3_is": "ARE", "adm0_a3_us": "ARE", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "Asia", "subregion": "Western Asia", "region_wb": "Middle East & North Africa", "name_len": 20.000000, "long_len": 20.000000, "abbrev_len": 6.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 51.5795, 24.2455 ], [ 51.7574, 24.2941 ], [ 51.7944, 24.0198 ], [ 52.5771, 24.1774 ], [ 53.404, 24.1513 ], [ 54.008, 24.1218 ], [ 54.693, 24.7979 ], [ 55.439, 25.4391 ], [ 56.0708, 26.0555 ], [ 56.261, 25.7146 ], [ 56.3968, 24.9247 ], [ 55.8862, 24.9208 ], [ 55.8041, 24.2696 ], [ 55.9812, 24.1305 ], [ 55.5286, 23.9336 ], [ 55.5258, 23.5249 ], [ 55.2345, 23.111 ], [ 55.2083, 22.7083 ], [ 55.0068, 22.4969 ], [ 52.0007, 23.0012 ], [ 51.6177, 24.0142 ], [ 51.5795, 24.2455 ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 2.000000, "type": "Sovereign country", "admin": "Argentina", "adm0_a3": "ARG", "name": "Argentina", "name_long": "Argentina", "abbrev": "Arg.", "postal": "AR", "formal_en": "Argentine Republic", "name_sort": "Argentina", "name_alt": null, "mapcolor7": 3.000000, "mapcolor8": 1.000000, "mapcolor9": 3.000000, "mapcolor13": 13.000000, "pop_est": 40913584.000000, "gdp_md_est": 573900.000000, "pop_year": -99.000000, "lastcensus": 2010.000000, "gdp_year": -99.000000, "economy": "5. Emerging region: G20", "income_grp": "3. Upper middle income", "iso_a2": "AR", "iso_a3": "ARG", "iso_n3": "032", "un_a3": "032", "wb_a2": "AR", "wb_a3": "ARG", "woe_id": -99.000000, "adm0_a3_is": "ARG", "adm0_a3_us": "ARG", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "South America", "subregion": "South America", "region_wb": "Latin America & Caribbean", "name_len": 9.000000, "long_len": 9.000000, "abbrev_len": 4.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -65.5, -55.2 ], [ -66.45, -55.25 ], [ -66.9599, -54.8968 ], [ -67.5624, -54.87 ], [ -68.6333, -54.8695 ], [ -68.634, -52.6364 ], [ -68.25, -53.1 ], [ -67.75, -53.85 ], [ -66.45, -54.45 ], [ -65.05, -54.7 ], [ -65.5, -55.2 ] ] ], [ [ [ -64.9649, -22.0759 ], [ -64.377, -22.7981 ], [ -63.9868, -21.9936 ], [ -62.8465, -22.035 ], [ -62.6851, -22.249 ], [ -60.8466, -23.8807 ], [ -60.029, -24.0328 ], [ -58.8071, -24.7715 ], [ -57.7772, -25.1623 ], [ -57.6337, -25.6037 ], [ -58.6182, -27.1237 ], [ -57.6098, -27.3959 ], [ -56.4867, -27.5485 ], [ -55.6958, -27.3878 ], [ -54.7888, -26.6218 ], [ -54.6253, -25.7393 ], [ -54.13, -25.5476 ], [ -53.6283, -26.1249 ], [ -53.6487, -26.9235 ], [ -54.4907, -27.4748 ], [ -55.1623, -27.8819 ], [ -56.2909, -28.8528 ], [ -57.6251, -30.2163 ], [ -57.8749, -31.0166 ], [ -58.1424, -32.0445 ], [ -58.1326, -33.0406 ], [ -58.3496, -33.2632 ], [ -58.4271, -33.9095 ], [ -58.4954, -34.4315 ], [ -57.2258, -35.288 ], [ -57.3624, -35.9774 ], [ -56.7375, -36.4131 ], [ -56.7883, -36.9016 ], [ -57.7492, -38.1839 ], [ -59.2319, -38.7202 ], [ -61.2374, -38.9284 ], [ -62.336, -38.8277 ], [ -62.1258, -39.4241 ], [ -62.3305, -40.1726 ], [ -62.146, -40.6769 ], [ -62.7458, -41.0288 ], [ -63.7705, -41.1668 ], [ -64.7321, -40.8027 ], [ -65.118, -41.0643 ], [ -64.9786, -42.058 ], [ -64.3034, -42.359 ], [ -63.7559, -42.0437 ], [ -63.4581, -42.5631 ], [ -64.3788, -42.8736 ], [ -65.1818, -43.4954 ], [ -65.3288, -44.5014 ], [ -65.5653, -45.0368 ], [ -66.51, -45.0396 ], [ -67.2938, -45.5519 ], [ -67.5805, -46.3018 ], [ -66.5971, -47.0339 ], [ -65.641, -47.2361 ], [ -65.9851, -48.1333 ], [ -67.1662, -48.6973 ], [ -67.8161, -49.8697 ], [ -68.7287, -50.2642 ], [ -69.1385, -50.7325 ], [ -68.8156, -51.7711 ], [ -68.15, -52.35 ], [ -68.5715, -52.2994 ], [ -69.4984, -52.1428 ], [ -71.9148, -52.009 ], [ -72.3294, -51.426 ], [ -72.31, -50.677 ], [ -72.9757, -50.7415 ], [ -73.3281, -50.3788 ], [ -73.4154, -49.3184 ], [ -72.6482, -48.8786 ], [ -72.3312, -48.2442 ], [ -72.4474, -47.7385 ], [ -71.9173, -46.8848 ], [ -71.552, -45.5607 ], [ -71.6593, -44.9737 ], [ -71.2228, -44.7842 ], [ -71.3298, -44.4075 ], [ -71.7936, -44.2072 ], [ -71.4641, -43.7876 ], [ -71.9154, -43.4086 ], [ -72.1489, -42.2549 ], [ -71.7468, -42.0514 ], [ -71.9157, -40.8323 ], [ -71.6808, -39.8082 ], [ -71.4135, -38.916 ], [ -70.8147, -38.553 ], [ -71.1186, -37.5768 ], [ -71.1219, -36.6581 ], [ -70.3648, -36.0051 ], [ -70.388, -35.1697 ], [ -69.8173, -34.1936 ], [ -69.8148, -33.2739 ], [ -70.0744, -33.0912 ], [ -70.5351, -31.365 ], [ -69.919, -30.3363 ], [ -70.0136, -29.3679 ], [ -69.6561, -28.4591 ], [ -69.0012, -27.5212 ], [ -68.2955, -26.8993 ], [ -68.5948, -26.5069 ], [ -68.386, -26.185 ], [ -68.4177, -24.5186 ], [ -67.3284, -24.0253 ], [ -66.9852, -22.9863 ], [ -67.1067, -22.7359 ], [ -66.2733, -21.8323 ], [ -64.9649, -22.0759 ] ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 6.000000, "type": "Sovereign country", "admin": "Armenia", "adm0_a3": "ARM", "name": "Armenia", "name_long": "Armenia", "abbrev": "Arm.", "postal": "ARM", "formal_en": "Republic of Armenia", "name_sort": "Armenia", "name_alt": null, "mapcolor7": 3.000000, "mapcolor8": 1.000000, "mapcolor9": 2.000000, "mapcolor13": 10.000000, "pop_est": 2967004.000000, "gdp_md_est": 18770.000000, "pop_year": -99.000000, "lastcensus": 2001.000000, "gdp_year": -99.000000, "economy": "6. Developing region", "income_grp": "4. Lower middle income", "iso_a2": "AM", "iso_a3": "ARM", "iso_n3": "051", "un_a3": "051", "wb_a2": "AM", "wb_a3": "ARM", "woe_id": -99.000000, "adm0_a3_is": "ARM", "adm0_a3_us": "ARM", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "Asia", "subregion": "Western Asia", "region_wb": "Europe & Central Asia", "name_len": 7.000000, "long_len": 7.000000, "abbrev_len": 4.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 43.5827, 41.0921 ], [ 44.9725, 41.2481 ], [ 45.1795, 40.9854 ], [ 45.5604, 40.8123 ], [ 45.3592, 40.5615 ], [ 45.8919, 40.2185 ], [ 45.61, 39.9 ], [ 46.0345, 39.628 ], [ 46.4835, 39.4642 ], [ 46.5057, 38.7706 ], [ 46.1436, 38.7412 ], [ 45.7354, 39.3197 ], [ 45.74, 39.474 ], [ 45.2981, 39.4718 ], [ 45.002, 39.74 ], [ 44.794, 39.713 ], [ 44.4, 40.005 ], [ 43.6564, 40.2536 ], [ 43.7527, 40.7402 ], [ 43.5827, 41.0921 ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 4.000000, "type": "Indeterminate", "admin": "Antarctica", "adm0_a3": "ATA", "name": "Antarctica", "name_long": "Antarctica", "abbrev": "Ant.", "postal": "AQ", "formal_en": null, "name_sort": "Antarctica", "name_alt": null, "mapcolor7": 4.000000, "mapcolor8": 5.000000, "mapcolor9": 1.000000, "mapcolor13": -99.000000, "pop_est": 3802.000000, "gdp_md_est": 760.400000, "pop_year": -99.000000, "lastcensus": -99.000000, "gdp_year": -99.000000, "economy": "6. Developing region", "income_grp": "2. High income: nonOECD", "iso_a2": "AQ", "iso_a3": "ATA", "iso_n3": "010", "un_a3": "-099", "wb_a2": "-99", "wb_a3": "-99", "woe_id": -99.000000, "adm0_a3_is": "ATA", "adm0_a3_us": "ATA", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "Antarctica", "subregion": "Antarctica", "region_wb": "Antarctica", "name_len": 10.000000, "long_len": 10.000000, "abbrev_len": 4.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -59.5721, -80.0402 ], [ -59.8658, -80.5497 ], [ -60.1597, -81.0003 ], [ -62.2554, -80.8632 ], [ -64.4881, -80.9219 ], [ -65.7417, -80.5888 ], [ -65.7417, -80.5497 ], [ -66.29, -80.2558 ], [ -64.0377, -80.2949 ], [ -61.8832, -80.3929 ], [ -61.139, -79.9814 ], [ -60.6101, -79.6287 ], [ -59.5721, -80.0402 ] ] ], [ [ [ -159.2082, -79.4971 ], [ -161.1276, -79.6342 ], [ -162.4398, -79.2815 ], [ -163.0274, -78.9288 ], [ -163.0666, -78.87 ], [ -163.7129, -78.5957 ], [ -163.1058, -78.2233 ], [ -161.2451, -78.3802 ], [ -160.2462, -78.6936 ], [ -159.4824, -79.0463 ], [ -159.2082, -79.4971 ] ] ], [ [ [ -45.1548, -78.0471 ], [ -43.9208, -78.4781 ], [ -43.4899, -79.0856 ], [ -43.3724, -79.5166 ], [ -43.3333, -80.0261 ], [ -44.8805, -80.3396 ], [ -46.5062, -80.5944 ], [ -48.3864, -80.8295 ], [ -50.4821, -81.0254 ], [ -52.852, -80.9667 ], [ -54.1643, -80.6335 ], [ -53.988, -80.222 ], [ -51.8531, -79.9477 ], [ -50.9913, -79.6146 ], [ -50.3646, -79.1835 ], [ -49.9141, -78.8112 ], [ -49.307, -78.4586 ], [ -48.6606, -78.047 ], [ -48.1514, -78.0471 ], [ -46.6629, -77.8315 ], [ -45.1548, -78.0471 ] ] ], [ [ [ -121.2115, -73.501 ], [ -119.9189, -73.6577 ], [ -118.7241, -73.4814 ], [ -119.2921, -73.8341 ], [ -120.2322, -74.0888 ], [ -121.6228, -74.0105 ], [ -122.6217, -73.6578 ], [ -122.4062, -73.3246 ], [ -121.2115, -73.501 ] ] ], [ [ [ -125.5596, -73.4814 ], [ -124.0319, -73.8733 ], [ -124.6195, -73.8341 ], [ -125.9122, -73.7361 ], [ -127.2831, -73.4618 ], [ -126.5585, -73.2462 ], [ -125.5596, -73.4814 ] ] ], [ [ [ -98.9815, -71.9333 ], [ -97.8847, -72.0705 ], [ -96.7879, -71.953 ], [ -96.2003, -72.5212 ], [ -96.9838, -72.4429 ], [ -98.1981, -72.482 ], [ -99.432, -72.4429 ], [ -100.7835, -72.5016 ], [ -101.8019, -72.3057 ], [ -102.3307, -71.8942 ], [ -101.704, -71.7178 ], [ -100.4309, -71.855 ], [ -98.9815, -71.9333 ] ] ], [ [ [ -68.4513, -70.9558 ], [ -68.3338, -71.4065 ], [ -68.5101, -71.7984 ], [ -68.7843, -72.1707 ], [ -69.9595, -72.3079 ], [ -71.0759, -72.5038 ], [ -72.3881, -72.4843 ], [ -71.8985, -72.0923 ], [ -73.0736, -72.2295 ], [ -74.19, -72.3667 ], [ -74.9539, -72.0728 ], [ -75.0126, -71.6613 ], [ -73.9158, -71.2693 ], [ -73.2303, -71.1518 ], [ -72.0747, -71.191 ], [ -71.781, -70.6815 ], [ -71.7222, -70.3092 ], [ -71.7418, -69.5058 ], [ -71.1738, -69.0355 ], [ -70.2533, -68.8787 ], [ -69.7244, -69.251 ], [ -69.4894, -69.6233 ], [ -69.0585, -70.074 ], [ -68.7255, -70.5052 ], [ -68.4513, -70.9558 ] ] ], [ [ [ -58.6141, -64.1525 ], [ -59.0451, -64.368 ], [ -59.7893, -64.2112 ], [ -60.6119, -64.3092 ], [ -61.2974, -64.5443 ], [ -62.0221, -64.7991 ], [ -62.5118, -65.093 ], [ -62.6489, -65.4849 ], [ -62.5901, -65.8572 ], [ -62.1201, -66.1903 ], [ -62.8056, -66.4255 ], [ -63.7457, -66.5038 ], [ -64.2941, -66.837 ], [ -64.8817, -67.1505 ], [ -65.5084, -67.5816 ], [ -65.6651, -67.9539 ], [ -65.3125, -68.3653 ], [ -64.7837, -68.6789 ], [ -63.9611, -68.914 ], [ -63.1973, -69.2276 ], [ -62.786, -69.6194 ], [ -62.5705, -69.9917 ], [ -62.2767, -70.3837 ], [ -61.8067, -70.7168 ], [ -61.5129, -71.089 ], [ -61.3758, -72.0101 ], [ -61.082, -72.3824 ], [ -61.0037, -72.7743 ], [ -60.6903, -73.1662 ], [ -60.8274, -73.6952 ], [ -61.3758, -74.1067 ], [ -61.9634, -74.4398 ], [ -63.2952, -74.577 ], [ -63.7457, -74.9297 ], [ -64.3528, -75.2628 ], [ -65.861, -75.6351 ], [ -67.1928, -75.7919 ], [ -68.4463, -76.0075 ], [ -69.7977, -76.223 ], [ -70.6007, -76.6345 ], [ -72.2068, -76.6737 ], [ -73.9695, -76.6345 ], [ -75.556, -76.7129 ], [ -77.2404, -76.7129 ], [ -76.927, -77.1048 ], [ -75.3993, -77.2811 ], [ -74.2829, -77.5554 ], [ -73.6561, -77.9081 ], [ -74.7725, -78.2216 ], [ -76.4961, -78.1237 ], [ -77.9259, -78.3784 ], [ -77.9847, -78.7899 ], [ -78.0238, -79.1818 ], [ -76.8486, -79.5149 ], [ -76.6332, -79.8872 ], [ -75.3601, -80.2595 ], [ -73.2449, -80.4163 ], [ -71.4429, -80.6906 ], [ -70.0132, -81.0042 ], [ -68.1916, -81.3177 ], [ -65.7043, -81.4745 ], [ -63.256, -81.7488 ], [ -61.552, -82.0427 ], [ -59.6914, -82.3759 ], [ -58.7121, -82.8461 ], [ -58.2225, -83.2184 ], [ -57.0081, -82.8657 ], [ -55.3629, -82.5718 ], [ -53.6198, -82.2582 ], [ -51.5436, -82.0035 ], [ -49.7613, -81.7292 ], [ -47.2739, -81.7096 ], [ -44.8257, -81.8467 ], [ -42.8084, -82.0819 ], [ -42.162, -81.6508 ], [ -40.7714, -81.3569 ], [ -38.2448, -81.3373 ], [ -36.2667, -81.1217 ], [ -34.3864, -80.9062 ], [ -32.3103, -80.769 ], [ -30.0971, -80.5927 ], [ -28.5498, -80.3379 ], [ -29.2549, -79.9852 ], [ -29.6858, -79.6325 ], [ -29.6858, -79.2602 ], [ -31.6248, -79.2994 ], [ -33.6813, -79.4561 ], [ -35.6399, -79.4561 ], [ -35.9141, -79.0839 ], [ -35.777, -78.3392 ], [ -35.3265, -78.1237 ], [ -33.8968, -77.8885 ], [ -32.2124, -77.6535 ], [ -29.7837, -77.0656 ], [ -28.8828, -76.6737 ], [ -27.5118, -76.4973 ], [ -26.1603, -76.3601 ], [ -25.4748, -76.2818 ], [ -23.9276, -76.2426 ], [ -22.4586, -76.1054 ], [ -21.2247, -75.9095 ], [ -20.0104, -75.6743 ], [ -18.9135, -75.4392 ], [ -17.523, -75.1257 ], [ -16.6416, -74.7925 ], [ -15.7015, -74.4986 ], [ -15.4077, -74.1067 ], [ -16.4653, -73.8716 ], [ -16.1128, -73.4601 ], [ -15.4469, -73.1465 ], [ -14.4088, -72.9506 ], [ -13.312, -72.7155 ], [ -12.2935, -72.4019 ], [ -11.5101, -72.0101 ], [ -11.0204, -71.5398 ], [ -10.2958, -71.2654 ], [ -9.101, -71.3242 ], [ -8.6114, -71.6573 ], [ -7.4166, -71.6965 ], [ -7.3775, -71.3242 ], [ -6.8682, -70.9323 ], [ -5.791, -71.0303 ], [ -5.5364, -71.4026 ], [ -4.3417, -71.4614 ], [ -3.049, -71.2851 ], [ -1.7955, -71.1674 ], [ -0.6595, -71.2262 ], [ -0.2286, -71.6377 ], [ 0.8682, -71.3046 ], [ 1.8867, -71.1283 ], [ 3.0226, -70.9911 ], [ 4.1391, -70.8539 ], [ 5.1575, -70.6188 ], [ 6.2739, -70.4621 ], [ 7.1357, -70.2465 ], [ 7.7429, -69.8938 ], [ 8.4871, -70.1485 ], [ 9.5251, -70.0113 ], [ 10.2498, -70.4816 ], [ 10.8178, -70.8343 ], [ 11.9538, -70.6384 ], [ 12.4043, -70.2465 ], [ 13.4228, -69.9722 ], [ 14.735, -70.0309 ], [ 15.1268, -70.4032 ], [ 15.9493, -70.0309 ], [ 17.0266, -69.9134 ], [ 18.2017, -69.8742 ], [ 19.2594, -69.8938 ], [ 20.3757, -70.0113 ], [ 21.453, -70.0701 ], [ 21.923, -70.4032 ], [ 22.5694, -70.6972 ], [ 23.6662, -70.5208 ], [ 24.8414, -70.4816 ], [ 25.9773, -70.4816 ], [ 27.0937, -70.4621 ], [ 28.0926, -70.3249 ], [ 29.1502, -70.2073 ], [ 30.0316, -69.9329 ], [ 30.9717, -69.7566 ], [ 31.9902, -69.6586 ], [ 32.7541, -69.3843 ], [ 33.3024, -68.8356 ], [ 33.8704, -68.5026 ], [ 34.9085, -68.6593 ], [ 35.3002, -69.012 ], [ 36.162, -69.2471 ], [ 37.2, -69.1687 ], [ 37.9051, -69.5214 ], [ 38.6494, -69.7762 ], [ 39.6679, -69.5411 ], [ 40.0204, -69.1099 ], [ 40.9214, -68.9336 ], [ 41.9594, -68.6005 ], [ 42.9387, -68.4633 ], [ 44.1139, -68.2674 ], [ 44.8973, -68.0519 ], [ 45.7199, -67.8167 ], [ 46.5033, -67.6012 ], [ 47.4434, -67.7188 ], [ 48.3444, -67.3661 ], [ 48.9907, -67.0917 ], [ 49.9309, -67.1113 ], [ 50.7535, -66.8762 ], [ 50.9493, -66.5235 ], [ 51.7915, -66.2491 ], [ 52.6141, -66.0532 ], [ 53.613, -65.8964 ], [ 54.5336, -65.818 ], [ 55.4149, -65.8768 ], [ 56.355, -65.9748 ], [ 57.1581, -66.2491 ], [ 57.256, -66.6802 ], [ 58.1374, -67.0133 ], [ 58.7445, -67.2877 ], [ 59.9393, -67.4052 ], [ 60.6052, -67.6796 ], [ 61.4278, -67.9539 ], [ 62.3875, -68.0127 ], [ 63.1905, -67.8167 ], [ 64.0523, -67.4052 ], [ 64.9924, -67.6207 ], [ 65.9717, -67.7383 ], [ 66.9119, -67.8559 ], [ 67.8911, -67.9343 ], [ 68.89, -67.9343 ], [ 69.7126, -68.9728 ], [ 69.6735, -69.2276 ], [ 69.5559, -69.6782 ], [ 68.5963, -69.9329 ], [ 67.8127, -70.3053 ], [ 67.9499, -70.6972 ], [ 69.0663, -70.6775 ], [ 68.9292, -71.0695 ], [ 68.42, -71.4418 ], [ 67.9499, -71.8533 ], [ 68.7138, -72.1668 ], [ 69.8693, -72.2648 ], [ 71.0249, -72.0884 ], [ 71.5733, -71.6965 ], [ 71.9063, -71.3242 ], [ 72.4546, -71.0107 ], [ 73.0814, -70.7168 ], [ 73.336, -70.364 ], [ 73.8649, -69.8742 ], [ 74.4916, -69.7762 ], [ 75.6276, -69.737 ], [ 76.6265, -69.6194 ], [ 77.6449, -69.4627 ], [ 78.1345, -69.0708 ], [ 78.4284, -68.6984 ], [ 79.1139, -68.3262 ], [ 80.0931, -68.0715 ], [ 80.9353, -67.8755 ], [ 81.4838, -67.5424 ], [ 82.0518, -67.3661 ], [ 82.7764, -67.2093 ], [ 83.7753, -67.3073 ], [ 84.6762, -67.2093 ], [ 85.6555, -67.0917 ], [ 86.7524, -67.1505 ], [ 87.477, -66.8762 ], [ 87.9863, -66.2099 ], [ 88.3584, -66.4843 ], [ 88.8284, -66.9546 ], [ 89.6706, -67.1505 ], [ 90.6304, -67.2289 ], [ 91.5901, -67.1113 ], [ 92.6085, -67.1897 ], [ 93.5486, -67.2093 ], [ 94.1754, -67.1113 ], [ 95.0176, -67.1701 ], [ 95.7815, -67.3857 ], [ 96.6824, -67.2485 ], [ 97.7596, -67.2485 ], [ 98.6802, -67.1113 ], [ 99.7182, -67.2485 ], [ 100.3842, -66.9153 ], [ 100.8934, -66.5822 ], [ 101.5789, -66.3079 ], [ 102.8324, -65.5633 ], [ 103.4787, -65.7005 ], [ 104.2426, -65.9748 ], [ 104.9085, -66.3275 ], [ 106.1816, -66.9349 ], [ 107.1609, -66.9546 ], [ 108.0814, -66.9546 ], [ 109.1586, -66.837 ], [ 110.2358, -66.6998 ], [ 111.0585, -66.4255 ], [ 111.744, -66.1316 ], [ 112.8604, -66.0923 ], [ 113.6047, -65.8768 ], [ 114.3881, -66.0728 ], [ 114.8973, -66.3863 ], [ 115.6024, -66.6998 ], [ 116.6992, -66.6606 ], [ 117.3847, -66.9153 ], [ 118.5795, -67.1701 ], [ 119.8329, -67.2681 ], [ 120.871, -67.1897 ], [ 121.6544, -66.8762 ], [ 122.3204, -66.5627 ], [ 123.2213, -66.4843 ], [ 124.1223, -66.6215 ], [ 125.1602, -66.7194 ], [ 126.1004, -66.5627 ], [ 127.0014, -66.5627 ], [ 127.8828, -66.6606 ], [ 128.8033, -66.7586 ], [ 129.7043, -66.5822 ], [ 130.7815, -66.4255 ], [ 131.7999, -66.3863 ], [ 132.9359, -66.3863 ], [ 133.8565, -66.2883 ], [ 134.7574, -66.21 ], [ 135.0316, -65.7201 ], [ 135.0708, -65.3086 ], [ 135.6975, -65.5829 ], [ 135.8738, -66.0336 ], [ 136.2067, -66.4451 ], [ 136.618, -66.7782 ], [ 137.4603, -66.9546 ], [ 138.5962, -66.8958 ], [ 139.9084, -66.8762 ], [ 140.8094, -66.8174 ], [ 142.1217, -66.8174 ], [ 143.0618, -66.7978 ], [ 144.3741, -66.837 ], [ 145.4904, -66.9153 ], [ 146.1956, -67.2289 ], [ 145.9997, -67.6012 ], [ 146.6461, -67.8951 ], [ 147.7233, -68.1303 ], [ 148.8396, -68.385 ], [ 150.1323, -68.5613 ], [ 151.4837, -68.7181 ], [ 152.5022, -68.8748 ], [ 153.6382, -68.8945 ], [ 154.2846, -68.5613 ], [ 155.1659, -68.8356 ], [ 155.9298, -69.1492 ], [ 156.8111, -69.3843 ], [ 158.0255, -69.4823 ], [ 159.181, -69.5998 ], [ 159.6707, -69.9917 ], [ 160.8067, -70.2269 ], [ 161.5705, -70.5796 ], [ 162.6869, -70.7364 ], [ 163.8424, -70.7168 ], [ 164.9197, -70.7755 ], [ 166.1144, -70.7559 ], [ 167.3091, -70.8343 ], [ 168.4256, -70.9715 ], [ 169.4636, -71.2067 ], [ 170.5017, -71.4026 ], [ 171.2068, -71.6965 ], [ 171.0892, -72.0884 ], [ 170.5604, -72.4412 ], [ 169.7574, -73.2445 ], [ 169.2873, -73.656 ], [ 167.9751, -73.8128 ], [ 167.3875, -74.1655 ], [ 166.0948, -74.381 ], [ 165.6444, -74.773 ], [ 164.9589, -75.1453 ], [ 164.2342, -75.4588 ], [ 163.8228, -75.8703 ], [ 163.5682, -76.2426 ], [ 163.4703, -76.6933 ], [ 163.4899, -77.0656 ], [ 164.0579, -77.4574 ], [ 164.2734, -77.8298 ], [ 164.7435, -78.1825 ], [ 166.6041, -78.3196 ], [ 166.9958, -78.7507 ], [ 165.1939, -78.9075 ], [ 163.6662, -79.123 ], [ 161.7664, -79.1622 ], [ 160.9242, -79.7305 ], [ 160.7479, -80.2007 ], [ 160.317, -80.5731 ], [ 159.7882, -80.9454 ], [ 161.12, -81.2785 ], [ 161.6293, -81.69 ], [ 162.491, -82.0623 ], [ 163.7053, -82.3954 ], [ 165.0959, -82.709 ], [ 166.6041, -83.0225 ], [ 168.8957, -83.336 ], [ 169.4048, -83.8259 ], [ 172.2839, -84.0414 ], [ 173.2241, -84.4137 ], [ 175.9857, -84.159 ], [ 178.2772, -84.4725 ], [ 180.0, -84.7134 ], [ 180.0, -90.0 ], [ -180.0, -90.0 ], [ -180.0, -84.7134 ], [ -179.9425, -84.7214 ], [ -179.0587, -84.1394 ], [ -177.2568, -84.4529 ], [ -176.0847, -84.0993 ], [ -175.9472, -84.1104 ], [ -175.8299, -84.1179 ], [ -174.3825, -84.5343 ], [ -173.1166, -84.1179 ], [ -172.8891, -84.061 ], [ -169.9512, -83.8846 ], [ -169.0, -84.1179 ], [ -168.5302, -84.2374 ], [ -167.0221, -84.5705 ], [ -164.1821, -84.8252 ], [ -161.9298, -85.1387 ], [ -158.0714, -85.3739 ], [ -155.1923, -85.0996 ], [ -150.9421, -85.2955 ], [ -148.5331, -85.609 ], [ -145.8889, -85.3151 ], [ -143.1077, -85.0408 ], [ -142.8923, -84.5705 ], [ -146.8291, -84.5313 ], [ -150.0607, -84.2961 ], [ -150.9029, -83.9042 ], [ -153.5862, -83.6887 ], [ -153.4099, -83.238 ], [ -153.0378, -82.8265 ], [ -152.6656, -82.4542 ], [ -152.8615, -82.0427 ], [ -154.5263, -81.7684 ], [ -155.2902, -81.4157 ], [ -156.8374, -81.1021 ], [ -154.4088, -81.1609 ], [ -152.0977, -81.0042 ], [ -150.6483, -81.3373 ], [ -148.866, -81.0434 ], [ -147.2207, -80.671 ], [ -146.4177, -80.3379 ], [ -146.7703, -79.9264 ], [ -148.0629, -79.6521 ], [ -149.5319, -79.3582 ], [ -151.5884, -79.2994 ], [ -153.3903, -79.1622 ], [ -155.3294, -79.0643 ], [ -155.9757, -78.6919 ], [ -157.2683, -78.3784 ], [ -158.0518, -78.0257 ], [ -158.3651, -76.8892 ], [ -157.8755, -76.9872 ], [ -156.9746, -77.3008 ], [ -155.3294, -77.2027 ], [ -153.7428, -77.0656 ], [ -152.9202, -77.4967 ], [ -151.3338, -77.3987 ], [ -150.0019, -77.1831 ], [ -148.7485, -76.9088 ], [ -147.6125, -76.5757 ], [ -146.1044, -76.4778 ], [ -146.1435, -76.1054 ], [ -146.4961, -75.7332 ], [ -146.2023, -75.3804 ], [ -144.9096, -75.204 ], [ -144.322, -75.5372 ], [ -142.7944, -75.3412 ], [ -141.6388, -75.0865 ], [ -140.209, -75.0669 ], [ -138.8576, -74.9689 ], [ -137.5062, -74.7338 ], [ -136.4289, -74.5182 ], [ -135.2146, -74.3027 ], [ -134.4312, -74.3615 ], [ -133.7457, -74.4398 ], [ -132.2572, -74.3027 ], [ -130.9253, -74.479 ], [ -129.5543, -74.4594 ], [ -128.242, -74.3223 ], [ -126.8906, -74.4203 ], [ -125.4021, -74.5182 ], [ -124.0115, -74.479 ], [ -122.5622, -74.4986 ], [ -121.0736, -74.5182 ], [ -119.7026, -74.479 ], [ -118.6841, -74.1851 ], [ -117.4698, -74.0283 ], [ -116.2163, -74.2439 ], [ -115.0216, -74.0675 ], [ -113.9443, -73.7148 ], [ -113.298, -74.0283 ], [ -112.9455, -74.381 ], [ -112.2991, -74.7142 ], [ -111.2611, -74.4203 ], [ -110.0663, -74.7925 ], [ -108.7149, -74.9101 ], [ -107.5593, -75.1845 ], [ -106.1491, -75.1257 ], [ -104.8761, -74.9493 ], [ -103.3679, -74.9885 ], [ -102.0165, -75.1257 ], [ -100.6455, -75.302 ], [ -100.1167, -74.8709 ], [ -100.763, -74.5378 ], [ -101.2527, -74.1851 ], [ -102.5453, -74.1067 ], [ -103.1133, -73.7344 ], [ -103.3288, -73.3621 ], [ -103.6813, -72.6175 ], [ -102.9175, -72.7547 ], [ -101.6052, -72.8134 ], [ -100.3125, -72.7547 ], [ -99.1374, -72.9114 ], [ -98.1189, -73.2053 ], [ -97.688, -73.558 ], [ -96.3366, -73.6168 ], [ -95.044, -73.4797 ], [ -93.6729, -73.2837 ], [ -92.439, -73.1662 ], [ -91.4206, -73.4013 ], [ -90.0887, -73.3229 ], [ -89.227, -72.5587 ], [ -88.424, -73.0094 ], [ -87.2683, -73.1858 ], [ -86.0148, -73.0878 ], [ -85.1922, -73.4797 ], [ -83.88, -73.5189 ], [ -82.6656, -73.6364 ], [ -81.4709, -73.852 ], [ -80.6874, -73.4797 ], [ -80.2958, -73.127 ], [ -79.2969, -73.5189 ], [ -77.9259, -73.4209 ], [ -76.9074, -73.6364 ], [ -76.2219, -73.9695 ], [ -74.89, -73.8716 ], [ -73.852, -73.656 ], [ -72.8335, -73.4013 ], [ -71.6192, -73.2642 ], [ -70.209, -73.1465 ], [ -68.9359, -73.0094 ], [ -67.9566, -72.7939 ], [ -67.3691, -72.4803 ], [ -67.134, -72.0492 ], [ -67.2515, -71.6377 ], [ -67.5649, -71.2458 ], [ -67.9175, -70.8539 ], [ -68.2308, -70.4621 ], [ -68.4855, -70.1093 ], [ -68.5442, -69.7174 ], [ -68.4463, -69.3255 ], [ -67.9762, -68.9532 ], [ -67.5845, -68.5417 ], [ -67.4278, -68.1498 ], [ -67.6237, -67.7188 ], [ -67.7412, -67.3268 ], [ -67.2515, -66.8762 ], [ -66.7032, -66.5822 ], [ -66.0568, -66.21 ], [ -65.3713, -65.8964 ], [ -64.5683, -65.6025 ], [ -64.1765, -65.1714 ], [ -63.6282, -64.8971 ], [ -63.0014, -64.6423 ], [ -62.0417, -64.5836 ], [ -61.4149, -64.27 ], [ -60.7099, -64.0741 ], [ -59.8873, -63.9565 ], [ -59.1626, -63.7017 ], [ -58.5946, -63.3882 ], [ -57.8111, -63.2707 ], [ -57.2236, -63.5254 ], [ -57.5957, -63.8585 ], [ -58.6141, -64.1525 ] ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 3, "labelrank": 6.000000, "type": "Dependency", "admin": "French Southern and Antarctic Lands", "adm0_a3": "ATF", "name": "Fr. S. Antarctic Lands", "name_long": "French Southern and Antarctic Lands", "abbrev": "Fr. S.A.L.", "postal": "TF", "formal_en": "Territory of the French Southern and Antarctic Lands", "name_sort": "French Southern and Antarctic Lands", "name_alt": null, "mapcolor7": 7.000000, "mapcolor8": 5.000000, "mapcolor9": 9.000000, "mapcolor13": 11.000000, "pop_est": 140.000000, "gdp_md_est": 16.000000, "pop_year": -99.000000, "lastcensus": -99.000000, "gdp_year": -99.000000, "economy": "6. Developing region", "income_grp": "2. High income: nonOECD", "iso_a2": "TF", "iso_a3": "ATF", "iso_n3": "260", "un_a3": "-099", "wb_a2": "-99", "wb_a3": "-99", "woe_id": -99.000000, "adm0_a3_is": "ATF", "adm0_a3_us": "ATF", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "Seven seas (open ocean)", "subregion": "Seven seas (open ocean)", "region_wb": "Sub-Saharan Africa", "name_len": 22.000000, "long_len": 35.000000, "abbrev_len": 10.000000, "tiny": 2.000000, "homepart": -99.000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 68.935, -48.625 ], [ 69.58, -48.94 ], [ 70.525, -49.065 ], [ 70.56, -49.255 ], [ 70.28, -49.71 ], [ 68.745, -49.775 ], [ 68.72, -49.2425 ], [ 68.8675, -48.83 ], [ 68.935, -48.625 ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 2.000000, "type": "Country", "admin": "Australia", "adm0_a3": "AUS", "name": "Australia", "name_long": "Australia", "abbrev": "Auz.", "postal": "AU", "formal_en": "Commonwealth of Australia", "name_sort": "Australia", "name_alt": null, "mapcolor7": 1.000000, "mapcolor8": 2.000000, "mapcolor9": 2.000000, "mapcolor13": 7.000000, "pop_est": 21262641.000000, "gdp_md_est": 800200.000000, "pop_year": -99.000000, "lastcensus": 2006.000000, "gdp_year": -99.000000, "economy": "2. Developed region: nonG7", "income_grp": "1. High income: OECD", "iso_a2": "AU", "iso_a3": "AUS", "iso_n3": "036", "un_a3": "036", "wb_a2": "AU", "wb_a3": "AUS", "woe_id": -99.000000, "adm0_a3_is": "AUS", "adm0_a3_us": "AUS", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "Oceania", "subregion": "Australia and New Zealand", "region_wb": "East Asia & Pacific", "name_len": 9.000000, "long_len": 9.000000, "abbrev_len": 4.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 145.398, -40.7925 ], [ 146.3641, -41.1377 ], [ 146.9086, -41.0005 ], [ 147.6893, -40.8083 ], [ 148.2891, -40.8754 ], [ 148.3599, -42.0624 ], [ 148.0173, -42.407 ], [ 147.9141, -43.2115 ], [ 147.5646, -42.9377 ], [ 146.8703, -43.6346 ], [ 146.6633, -43.5809 ], [ 146.0484, -43.5497 ], [ 145.4319, -42.6938 ], [ 145.2951, -42.0336 ], [ 144.7181, -41.1626 ], [ 144.7438, -40.704 ], [ 145.398, -40.7925 ] ] ], [ [ [ 143.5618, -13.7637 ], [ 143.9221, -14.5483 ], [ 144.5637, -14.1712 ], [ 144.8949, -14.5945 ], [ 145.3747, -14.985 ], [ 145.272, -15.4282 ], [ 145.4853, -16.2857 ], [ 145.637, -16.7849 ], [ 145.8889, -16.9069 ], [ 146.1603, -17.7617 ], [ 146.0637, -18.2801 ], [ 146.3875, -18.9583 ], [ 147.4711, -19.4807 ], [ 148.1776, -19.9559 ], [ 148.8484, -20.3912 ], [ 148.7175, -20.6335 ], [ 149.2894, -21.2605 ], [ 149.6783, -22.3425 ], [ 150.0774, -22.1228 ], [ 150.4829, -22.5561 ], [ 150.7273, -22.4024 ], [ 150.8996, -23.4622 ], [ 151.6092, -24.0763 ], [ 152.0735, -24.4579 ], [ 152.8552, -25.2675 ], [ 153.1362, -26.0712 ], [ 153.1619, -26.6413 ], [ 153.0929, -27.2603 ], [ 153.5695, -28.1101 ], [ 153.5121, -28.9951 ], [ 153.3391, -29.4582 ], [ 153.0692, -30.3502 ], [ 153.0896, -30.9236 ], [ 152.8916, -31.6404 ], [ 152.45, -32.55 ], [ 151.7091, -33.0413 ], [ 151.344, -33.816 ], [ 151.0106, -34.3104 ], [ 150.7141, -35.1735 ], [ 150.3282, -35.6719 ], [ 150.0752, -36.4202 ], [ 149.9461, -37.1091 ], [ 149.9973, -37.4253 ], [ 149.4239, -37.7727 ], [ 148.3046, -37.8091 ], [ 147.3817, -38.2192 ], [ 146.9221, -38.6065 ], [ 146.3179, -39.0358 ], [ 145.4897, -38.5938 ], [ 144.877, -38.4174 ], [ 145.0322, -37.8962 ], [ 144.4857, -38.0853 ], [ 143.61, -38.8095 ], [ 142.7454, -38.5383 ], [ 142.1783, -38.38 ], [ 141.6066, -38.3085 ], [ 140.6386, -38.0193 ], [ 139.9922, -37.4029 ], [ 139.8066, -36.6436 ], [ 139.5741, -36.1384 ], [ 139.0828, -35.7328 ], [ 138.1207, -35.6123 ], [ 138.4495, -35.1273 ], [ 138.2076, -34.3847 ], [ 137.7192, -35.0768 ], [ 136.8294, -35.2605 ], [ 137.3524, -34.7073 ], [ 137.5039, -34.1303 ], [ 137.8901, -33.6405 ], [ 137.8103, -32.9 ], [ 136.9968, -33.7528 ], [ 136.3721, -34.0948 ], [ 135.989, -34.8901 ], [ 135.2082, -34.4787 ], [ 135.2392, -33.948 ], [ 134.6134, -33.2228 ], [ 134.0859, -32.8481 ], [ 134.2739, -32.6172 ], [ 132.9908, -32.0112 ], [ 132.2881, -31.9826 ], [ 131.3263, -31.4958 ], [ 129.5358, -31.5904 ], [ 128.2409, -31.9485 ], [ 127.1029, -32.2823 ], [ 126.1487, -32.216 ], [ 125.0886, -32.7288 ], [ 124.2216, -32.9595 ], [ 124.0289, -33.4838 ], [ 123.6597, -33.8902 ], [ 122.811, -33.9145 ], [ 122.1831, -34.0034 ], [ 121.2992, -33.821 ], [ 120.5803, -33.9302 ], [ 119.8937, -33.9761 ], [ 119.2989, -34.5094 ], [ 119.0073, -34.4641 ], [ 118.5057, -34.7468 ], [ 118.025, -35.0647 ], [ 117.2955, -35.0255 ], [ 116.6251, -35.0251 ], [ 115.5643, -34.3864 ], [ 115.0268, -34.1965 ], [ 115.0486, -33.6234 ], [ 115.5451, -33.4873 ], [ 115.7147, -33.2596 ], [ 115.6794, -32.9004 ], [ 115.8016, -32.2051 ], [ 115.6896, -31.6124 ], [ 115.1609, -30.6016 ], [ 114.997, -30.0307 ], [ 115.04, -29.4611 ], [ 114.642, -28.8102 ], [ 114.6165, -28.5164 ], [ 114.1736, -28.1181 ], [ 114.0489, -27.3348 ], [ 113.4775, -26.5431 ], [ 113.339, -26.1165 ], [ 113.7784, -26.549 ], [ 113.441, -25.6213 ], [ 113.9369, -25.9112 ], [ 114.2329, -26.2984 ], [ 114.2162, -25.7863 ], [ 113.7213, -24.9989 ], [ 113.6253, -24.684 ], [ 113.3935, -24.3848 ], [ 113.502, -23.8064 ], [ 113.707, -23.5602 ], [ 113.8434, -23.06 ], [ 113.7366, -22.4755 ], [ 114.1498, -21.7559 ], [ 114.2253, -22.5175 ], [ 114.6478, -21.8295 ], [ 115.4602, -21.4952 ], [ 115.9474, -21.0687 ], [ 116.7116, -20.7017 ], [ 117.1663, -20.6236 ], [ 117.4415, -20.7469 ], [ 118.2296, -20.3742 ], [ 118.8361, -20.2633 ], [ 118.9878, -20.0442 ], [ 119.2525, -19.9529 ], [ 119.8052, -19.9765 ], [ 120.8562, -19.6837 ], [ 121.3999, -19.2398 ], [ 121.6551, -18.7053 ], [ 122.2417, -18.1976 ], [ 122.2866, -17.7986 ], [ 122.3128, -17.255 ], [ 123.0126, -16.4052 ], [ 123.4338, -17.2686 ], [ 123.8593, -17.069 ], [ 123.5032, -16.5965 ], [ 123.8171, -16.1113 ], [ 124.2583, -16.3279 ], [ 124.3797, -15.5671 ], [ 124.9262, -15.0751 ], [ 125.1673, -14.6804 ], [ 125.6701, -14.5101 ], [ 125.6858, -14.2307 ], [ 126.1251, -14.3473 ], [ 126.1428, -14.096 ], [ 126.5826, -13.9528 ], [ 127.0659, -13.818 ], [ 127.8046, -14.2769 ], [ 128.3597, -14.8692 ], [ 128.9855, -14.876 ], [ 129.6215, -14.9698 ], [ 129.4096, -14.4207 ], [ 129.8886, -13.6187 ], [ 130.3395, -13.3574 ], [ 130.1835, -13.1075 ], [ 130.6178, -12.5364 ], [ 131.2235, -12.1836 ], [ 131.7351, -12.3025 ], [ 132.5753, -12.114 ], [ 132.5572, -11.603 ], [ 131.8247, -11.2738 ], [ 132.3572, -11.1285 ], [ 133.0196, -11.3764 ], [ 133.5508, -11.7865 ], [ 134.3931, -12.0424 ], [ 134.6786, -11.9412 ], [ 135.2985, -12.2486 ], [ 135.8827, -11.9623 ], [ 136.2584, -12.0493 ], [ 136.4925, -11.8572 ], [ 136.9516, -12.352 ], [ 136.6851, -12.8872 ], [ 136.3054, -13.2912 ], [ 135.9618, -13.3245 ], [ 136.0776, -13.7243 ], [ 135.7838, -14.224 ], [ 135.4287, -14.7154 ], [ 135.5002, -14.9977 ], [ 136.2952, -15.5503 ], [ 137.0654, -15.8708 ], [ 137.5805, -16.2151 ], [ 138.3032, -16.8076 ], [ 138.5852, -16.8066 ], [ 139.1085, -17.0627 ], [ 139.2606, -17.3716 ], [ 140.2152, -17.7108 ], [ 140.8755, -17.3691 ], [ 141.0711, -16.832 ], [ 141.2741, -16.3889 ], [ 141.3982, -15.8405 ], [ 141.7022, -15.0449 ], [ 141.5634, -14.5613 ], [ 141.6355, -14.2704 ], [ 141.5199, -13.6981 ], [ 141.6509, -12.9447 ], [ 141.8427, -12.7415 ], [ 141.687, -12.4076 ], [ 141.9286, -11.8775 ], [ 142.1185, -11.328 ], [ 142.1437, -11.0427 ], [ 142.5153, -10.6682 ], [ 142.7973, -11.1574 ], [ 142.8668, -11.7847 ], [ 143.1159, -11.9056 ], [ 143.1586, -12.3257 ], [ 143.5221, -12.8344 ], [ 143.5972, -13.4004 ], [ 143.5618, -13.7637 ] ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 4.000000, "type": "Sovereign country", "admin": "Austria", "adm0_a3": "AUT", "name": "Austria", "name_long": "Austria", "abbrev": "Aust.", "postal": "A", "formal_en": "Republic of Austria", "name_sort": "Austria", "name_alt": null, "mapcolor7": 3.000000, "mapcolor8": 1.000000, "mapcolor9": 3.000000, "mapcolor13": 4.000000, "pop_est": 8210281.000000, "gdp_md_est": 329500.000000, "pop_year": -99.000000, "lastcensus": 2011.000000, "gdp_year": -99.000000, "economy": "2. Developed region: nonG7", "income_grp": "1. High income: OECD", "iso_a2": "AT", "iso_a3": "AUT", "iso_n3": "040", "un_a3": "040", "wb_a2": "AT", "wb_a3": "AUT", "woe_id": -99.000000, "adm0_a3_is": "AUT", "adm0_a3_us": "AUT", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "Europe", "subregion": "Western Europe", "region_wb": "Europe & Central Asia", "name_len": 7.000000, "long_len": 7.000000, "abbrev_len": 5.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 16.9797, 48.1235 ], [ 16.9038, 47.7149 ], [ 16.3406, 47.7129 ], [ 16.5343, 47.4962 ], [ 16.2023, 46.8524 ], [ 16.0117, 46.6836 ], [ 15.1371, 46.6587 ], [ 14.6325, 46.4318 ], [ 13.8065, 46.5093 ], [ 12.3765, 46.7676 ], [ 12.1531, 47.1154 ], [ 11.1648, 46.9416 ], [ 11.0486, 46.7514 ], [ 10.4427, 46.8935 ], [ 9.9324, 46.9207 ], [ 9.48, 47.1028 ], [ 9.6329, 47.3476 ], [ 9.5942, 47.5251 ], [ 9.8961, 47.5802 ], [ 10.4021, 47.3025 ], [ 10.5445, 47.5664 ], [ 11.4264, 47.5238 ], [ 12.1414, 47.7031 ], [ 12.6208, 47.6724 ], [ 12.9326, 47.4676 ], [ 13.0259, 47.6376 ], [ 12.8841, 48.2891 ], [ 13.2434, 48.4161 ], [ 13.5959, 48.8772 ], [ 14.3389, 48.5553 ], [ 14.9014, 48.9644 ], [ 15.2534, 49.0391 ], [ 16.0296, 48.7339 ], [ 16.4993, 48.7858 ], [ 16.9603, 48.597 ], [ 16.88, 48.47 ], [ 16.9797, 48.1235 ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 5.000000, "type": "Sovereign country", "admin": "Azerbaijan", "adm0_a3": "AZE", "name": "Azerbaijan", "name_long": "Azerbaijan", "abbrev": "Aze.", "postal": "AZ", "formal_en": "Republic of Azerbaijan", "name_sort": "Azerbaijan", "name_alt": null, "mapcolor7": 1.000000, "mapcolor8": 6.000000, "mapcolor9": 5.000000, "mapcolor13": 8.000000, "pop_est": 8238672.000000, "gdp_md_est": 77610.000000, "pop_year": -99.000000, "lastcensus": 2009.000000, "gdp_year": -99.000000, "economy": "6. Developing region", "income_grp": "3. Upper middle income", "iso_a2": "AZ", "iso_a3": "AZE", "iso_n3": "031", "un_a3": "031", "wb_a2": "AZ", "wb_a3": "AZE", "woe_id": -99.000000, "adm0_a3_is": "AZE", "adm0_a3_us": "AZE", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "Asia", "subregion": "Western Asia", "region_wb": "Europe & Central Asia", "name_len": 10.000000, "long_len": 10.000000, "abbrev_len": 4.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 45.002, 39.74 ], [ 45.2981, 39.4718 ], [ 45.74, 39.474 ], [ 45.7354, 39.3197 ], [ 46.1436, 38.7412 ], [ 45.4577, 38.8741 ], [ 44.9527, 39.3358 ], [ 44.794, 39.713 ], [ 45.002, 39.74 ] ] ], [ [ [ 47.3733, 41.2197 ], [ 47.8157, 41.1514 ], [ 47.9873, 41.4058 ], [ 48.5844, 41.8089 ], [ 49.1103, 41.2823 ], [ 49.6189, 40.5729 ], [ 50.0848, 40.5262 ], [ 50.3928, 40.2566 ], [ 49.5692, 40.1761 ], [ 49.3953, 39.3995 ], [ 49.2232, 39.0492 ], [ 48.8565, 38.8155 ], [ 48.8832, 38.3202 ], [ 48.6344, 38.2704 ], [ 48.0107, 38.794 ], [ 48.3555, 39.2888 ], [ 48.0601, 39.5822 ], [ 47.6851, 39.5084 ], [ 46.5057, 38.7706 ], [ 46.4835, 39.4642 ], [ 46.0345, 39.628 ], [ 45.61, 39.9 ], [ 45.8919, 40.2185 ], [ 45.3592, 40.5615 ], [ 45.5604, 40.8123 ], [ 45.1795, 40.9854 ], [ 44.9725, 41.2481 ], [ 45.2174, 41.4115 ], [ 45.9626, 41.1239 ], [ 46.5016, 41.0644 ], [ 46.6379, 41.1817 ], [ 46.1454, 41.7228 ], [ 46.405, 41.8607 ], [ 46.6861, 41.8271 ], [ 47.3733, 41.2197 ] ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 6.000000, "type": "Sovereign country", "admin": "Burundi", "adm0_a3": "BDI", "name": "Burundi", "name_long": "Burundi", "abbrev": "Bur.", "postal": "BI", "formal_en": "Republic of Burundi", "name_sort": "Burundi", "name_alt": null, "mapcolor7": 2.000000, "mapcolor8": 2.000000, "mapcolor9": 5.000000, "mapcolor13": 8.000000, "pop_est": 8988091.000000, "gdp_md_est": 3102.000000, "pop_year": -99.000000, "lastcensus": 2008.000000, "gdp_year": -99.000000, "economy": "7. Least developed region", "income_grp": "5. Low income", "iso_a2": "BI", "iso_a3": "BDI", "iso_n3": "108", "un_a3": "108", "wb_a2": "BI", "wb_a3": "BDI", "woe_id": -99.000000, "adm0_a3_is": "BDI", "adm0_a3_us": "BDI", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "Africa", "subregion": "Eastern Africa", "region_wb": "Sub-Saharan Africa", "name_len": 7.000000, "long_len": 7.000000, "abbrev_len": 4.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 29.34, -4.5 ], [ 29.2764, -3.2939 ], [ 29.0249, -2.8393 ], [ 29.6322, -2.9179 ], [ 29.9384, -2.3485 ], [ 30.4697, -2.4139 ], [ 30.5277, -2.8076 ], [ 30.743, -3.0343 ], [ 30.7523, -3.3593 ], [ 30.5056, -3.5686 ], [ 30.1163, -4.0901 ], [ 29.7535, -4.4524 ], [ 29.34, -4.5 ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 2.000000, "type": "Sovereign country", "admin": "Belgium", "adm0_a3": "BEL", "name": "Belgium", "name_long": "Belgium", "abbrev": "Belg.", "postal": "B", "formal_en": "Kingdom of Belgium", "name_sort": "Belgium", "name_alt": null, "mapcolor7": 3.000000, "mapcolor8": 2.000000, "mapcolor9": 1.000000, "mapcolor13": 8.000000, "pop_est": 10414336.000000, "gdp_md_est": 389300.000000, "pop_year": -99.000000, "lastcensus": 2011.000000, "gdp_year": -99.000000, "economy": "2. Developed region: nonG7", "income_grp": "1. High income: OECD", "iso_a2": "BE", "iso_a3": "BEL", "iso_n3": "056", "un_a3": "056", "wb_a2": "BE", "wb_a3": "BEL", "woe_id": -99.000000, "adm0_a3_is": "BEL", "adm0_a3_us": "BEL", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "Europe", "subregion": "Western Europe", "region_wb": "Europe & Central Asia", "name_len": 7.000000, "long_len": 7.000000, "abbrev_len": 5.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3.315, 51.3458 ], [ 4.0471, 51.2673 ], [ 4.974, 51.475 ], [ 5.607, 51.0373 ], [ 6.1567, 50.8037 ], [ 6.0431, 50.1281 ], [ 5.7824, 50.0903 ], [ 5.6741, 49.5295 ], [ 4.7992, 49.9854 ], [ 4.286, 49.9075 ], [ 3.5882, 50.379 ], [ 3.1233, 50.7804 ], [ 2.6584, 50.7968 ], [ 2.5136, 51.1485 ], [ 3.315, 51.3458 ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 5.000000, "type": "Sovereign country", "admin": "Benin", "adm0_a3": "BEN", "name": "Benin", "name_long": "Benin", "abbrev": "Benin", "postal": "BJ", "formal_en": "Republic of Benin", "name_sort": "Benin", "name_alt": null, "mapcolor7": 1.000000, "mapcolor8": 2.000000, "mapcolor9": 2.000000, "mapcolor13": 12.000000, "pop_est": 8791832.000000, "gdp_md_est": 12830.000000, "pop_year": -99.000000, "lastcensus": 2002.000000, "gdp_year": -99.000000, "economy": "7. Least developed region", "income_grp": "5. Low income", "iso_a2": "BJ", "iso_a3": "BEN", "iso_n3": "204", "un_a3": "204", "wb_a2": "BJ", "wb_a3": "BEN", "woe_id": -99.000000, "adm0_a3_is": "BEN", "adm0_a3_us": "BEN", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "Africa", "subregion": "Western Africa", "region_wb": "Sub-Saharan Africa", "name_len": 5.000000, "long_len": 5.000000, "abbrev_len": 5.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 2.6917, 6.2588 ], [ 1.8652, 6.1422 ], [ 1.619, 6.832 ], [ 1.6645, 9.1286 ], [ 1.463, 9.3346 ], [ 1.4251, 9.8254 ], [ 1.0778, 10.1756 ], [ 0.7723, 10.4708 ], [ 0.8996, 10.9973 ], [ 1.2435, 11.1105 ], [ 1.4472, 11.5477 ], [ 1.936, 11.6412 ], [ 2.1545, 11.9402 ], [ 2.4902, 12.2331 ], [ 2.8486, 12.2356 ], [ 3.6112, 11.6602 ], [ 3.5722, 11.3279 ], [ 3.7971, 10.7347 ], [ 3.6001, 10.3322 ], [ 3.7054, 10.0632 ], [ 3.2204, 9.4442 ], [ 2.9123, 9.1376 ], [ 2.7238, 8.5068 ], [ 2.7491, 7.8707 ], [ 2.6917, 6.2588 ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 3.000000, "type": "Sovereign country", "admin": "Burkina Faso", "adm0_a3": "BFA", "name": "Burkina Faso", "name_long": "Burkina Faso", "abbrev": "B.F.", "postal": "BF", "formal_en": "Burkina Faso", "name_sort": "Burkina Faso", "name_alt": null, "mapcolor7": 2.000000, "mapcolor8": 1.000000, "mapcolor9": 5.000000, "mapcolor13": 11.000000, "pop_est": 15746232.000000, "gdp_md_est": 17820.000000, "pop_year": -99.000000, "lastcensus": 2006.000000, "gdp_year": -99.000000, "economy": "7. Least developed region", "income_grp": "5. Low income", "iso_a2": "BF", "iso_a3": "BFA", "iso_n3": "854", "un_a3": "854", "wb_a2": "BF", "wb_a3": "BFA", "woe_id": -99.000000, "adm0_a3_is": "BFA", "adm0_a3_us": "BFA", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "Africa", "subregion": "Western Africa", "region_wb": "Sub-Saharan Africa", "name_len": 12.000000, "long_len": 12.000000, "abbrev_len": 4.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -2.8275, 9.6425 ], [ -3.5119, 9.9003 ], [ -3.9804, 9.8623 ], [ -4.3302, 9.6108 ], [ -4.7799, 9.822 ], [ -4.9547, 10.1527 ], [ -5.4043, 10.3707 ], [ -5.4706, 10.9513 ], [ -5.1978, 11.3751 ], [ -5.2209, 11.7139 ], [ -4.4272, 12.5426 ], [ -4.2804, 13.2284 ], [ -4.0064, 13.4725 ], [ -3.5228, 13.3377 ], [ -3.1037, 13.5413 ], [ -2.9677, 13.7982 ], [ -2.1918, 14.2464 ], [ -2.001, 14.559 ], [ -1.0664, 14.9738 ], [ -0.5159, 15.1162 ], [ -0.2663, 14.9243 ], [ 0.3749, 14.9289 ], [ 0.2956, 14.4442 ], [ 0.4299, 13.9887 ], [ 0.993, 13.3357 ], [ 1.0241, 12.8518 ], [ 2.1771, 12.625 ], [ 2.1545, 11.9402 ], [ 1.936, 11.6412 ], [ 1.4472, 11.5477 ], [ 1.2435, 11.1105 ], [ 0.8996, 10.9973 ], [ 0.0238, 11.0187 ], [ -0.4387, 11.0983 ], [ -0.7616, 10.9369 ], [ -1.2034, 11.0098 ], [ -2.9404, 10.9627 ], [ -2.9639, 10.3953 ], [ -2.8275, 9.6425 ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 3.000000, "type": "Sovereign country", "admin": "Bangladesh", "adm0_a3": "BGD", "name": "Bangladesh", "name_long": "Bangladesh", "abbrev": "Bang.", "postal": "BD", "formal_en": "People's Republic of Bangladesh", "name_sort": "Bangladesh", "name_alt": null, "mapcolor7": 3.000000, "mapcolor8": 4.000000, "mapcolor9": 7.000000, "mapcolor13": 7.000000, "pop_est": 156050883.000000, "gdp_md_est": 224000.000000, "pop_year": -99.000000, "lastcensus": 2011.000000, "gdp_year": -99.000000, "economy": "7. Least developed region", "income_grp": "5. Low income", "iso_a2": "BD", "iso_a3": "BGD", "iso_n3": "050", "un_a3": "050", "wb_a2": "BD", "wb_a3": "BGD", "woe_id": -99.000000, "adm0_a3_is": "BGD", "adm0_a3_us": "BGD", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "Asia", "subregion": "Southern Asia", "region_wb": "South Asia", "name_len": 10.000000, "long_len": 10.000000, "abbrev_len": 5.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 92.6727, 22.0412 ], [ 92.6523, 21.324 ], [ 92.3032, 21.4755 ], [ 92.3686, 20.6709 ], [ 92.0829, 21.1922 ], [ 92.0252, 21.7016 ], [ 91.8349, 22.1829 ], [ 91.4171, 22.765 ], [ 90.496, 22.805 ], [ 90.587, 22.3928 ], [ 90.273, 21.8364 ], [ 89.8475, 22.0391 ], [ 89.702, 21.8571 ], [ 89.4189, 21.9662 ], [ 89.032, 22.0557 ], [ 88.8763, 22.8791 ], [ 88.5298, 23.6311 ], [ 88.6999, 24.2337 ], [ 88.0844, 24.5017 ], [ 88.3064, 24.8661 ], [ 88.9316, 25.2387 ], [ 88.2098, 25.7681 ], [ 88.563, 26.4465 ], [ 89.3551, 26.0144 ], [ 89.8325, 25.9651 ], [ 89.9207, 25.2697 ], [ 90.8722, 25.1326 ], [ 91.7996, 25.1474 ], [ 92.3762, 24.9767 ], [ 91.9151, 24.1304 ], [ 91.4677, 24.0726 ], [ 91.159, 23.5035 ], [ 91.7065, 22.9853 ], [ 91.8699, 23.6243 ], [ 92.146, 23.6275 ], [ 92.6727, 22.0412 ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 4.000000, "type": "Sovereign country", "admin": "Bulgaria", "adm0_a3": "BGR", "name": "Bulgaria", "name_long": "Bulgaria", "abbrev": "Bulg.", "postal": "BG", "formal_en": "Republic of Bulgaria", "name_sort": "Bulgaria", "name_alt": null, "mapcolor7": 4.000000, "mapcolor8": 5.000000, "mapcolor9": 1.000000, "mapcolor13": 8.000000, "pop_est": 7204687.000000, "gdp_md_est": 93750.000000, "pop_year": -99.000000, "lastcensus": 2011.000000, "gdp_year": -99.000000, "economy": "2. Developed region: nonG7", "income_grp": "3. Upper middle income", "iso_a2": "BG", "iso_a3": "BGR", "iso_n3": "100", "un_a3": "100", "wb_a2": "BG", "wb_a3": "BGR", "woe_id": -99.000000, "adm0_a3_is": "BGR", "adm0_a3_us": "BGR", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "Europe", "subregion": "Eastern Europe", "region_wb": "Europe & Central Asia", "name_len": 8.000000, "long_len": 8.000000, "abbrev_len": 5.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 22.6571, 44.2349 ], [ 22.9448, 43.8238 ], [ 23.3323, 43.897 ], [ 24.1007, 43.7411 ], [ 25.5693, 43.6884 ], [ 26.0652, 43.9435 ], [ 27.2424, 44.176 ], [ 27.9701, 43.8125 ], [ 28.5581, 43.7075 ], [ 28.0391, 43.2932 ], [ 27.6739, 42.5779 ], [ 27.9967, 42.0074 ], [ 27.1357, 42.1415 ], [ 26.117, 41.8269 ], [ 26.1061, 41.3289 ], [ 25.1972, 41.2345 ], [ 24.4926, 41.5839 ], [ 23.6921, 41.3091 ], [ 22.9524, 41.338 ], [ 22.8814, 41.9993 ], [ 22.3805, 42.3203 ], [ 22.545, 42.4614 ], [ 22.4366, 42.5803 ], [ 22.6048, 42.8985 ], [ 22.986, 43.2112 ], [ 22.5002, 43.6428 ], [ 22.4104, 44.0081 ], [ 22.6571, 44.2349 ] ] ] } }, { "type": "Feature", "properties": { "scalerank": 1, "labelrank": 4.000000, "type": "Sovereign country", "admin": "The Bahamas", "adm0_a3": "BHS", "name": "Bahamas", "name_long": "Bahamas", "abbrev": "Bhs.", "postal": "BS", "formal_en": "Commonwealth of the Bahamas", "name_sort": "Bahamas, The", "name_alt": null, "mapcolor7": 1.000000, "mapcolor8": 1.000000, "mapcolor9": 2.000000, "mapcolor13": 5.000000, "pop_est": 309156.000000, "gdp_md_est": 9093.000000, "pop_year": -99.000000, "lastcensus": 2010.000000, "gdp_year": -99.000000, "economy": "6. Developing region", "income_grp": "2. High income: nonOECD", "iso_a2": "BS", "iso_a3": "BHS", "iso_n3": "044", "un_a3": "044", "wb_a2": "BS", "wb_a3": "BHS", "woe_id": -99.000000, "adm0_a3_is": "BHS", "adm0_a3_us": "BHS", "adm0_a3_un": -99.000000, "adm0_a3_wb": -99.000000, "continent": "North America", "subregion": "Caribbean", "region_wb": "Latin America & Caribbean", "name_len": 7.000000, "long_len": 7.000000, "abbrev_len": 4.000000, "tiny": -99.000000, "homepart": 1.000000 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -77.5347, 23.7597 ], [ -77.78, 23.71 ], [ -78.0341, 24.2862 ], [ -78.4085, 24.5756 ], [ -78.1909, 25.2103 ], [ -77.89, 25.17 ], [ -77.54, 24.34 ], [ -77.5347, 23.7597 ] ] ], [ [ [ -77.82, 26.58 ], [ -78.91, 26.42 ], [ -78.98, 26.79 ], [ -78.51, 26.87 ], [ -77.85, 26.84 ], [ -77.82, 26.58 ] ] ], [ [ [ -77.0, 26.59 ], [ -77.1725, 25.8792 ], [ -77.3564, 26.0073 ], [ -77.34, 26.53 ], [ -77.788, 26.9252 ], [ -77.79, 27.04 ], [ -77.0, 26.59 ] ] ] ] } } ] } MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5))) >@4@$@D@F@D@>@4@.@@D@$@$@4@@$@.@@ POLYGON ((30 10 , 10 20 , 20 40 , 40 40 , 30 10 )) The Pentagon 1 relativeToGround -77.05788457660967,38.87253259892824,100 -77.05465973756702,38.87291016281703,100 -77.05315536854791,38.87053267794386,100 -77.05552622493516,38.868757801256,100 -77.05844056290393,38.86996206506943,100 -77.05788457660967,38.87253259892824,100 -77.05668055019126,38.87154239798456,100 -77.05542625960818,38.87167890344077,100 -77.05485125901024,38.87076535397792,100 -77.05577677433152,38.87008686581446,100 -77.05691162017543,38.87054446963351,100 -77.05668055019126,38.87154239798456,100 {"type": "Feature", "geometry": {"type": "Point", "coordinates": [-80.73029, 35.3936]}} 45.256 -110.45 46.46 -109.48 43.84 -109.86 45.256 -110.45 SRID=4269;MULTILINESTRING((-71.160281 42.258729,-71.160837 42.259113,-71.161144 42.25932)) 42.943 -71.032 43.039 -69.856 POINT EMPTYxpssc0 Chris Archer Endomondo http://www.endomondo.com/ endomondo MOUNTAIN_BIKING 121.0 121.0 122.0 122.0 122.0 123.0 124.0 130.0 131.0 131.0 132.0 131.0 132.0 129.0 130.0 125.0 122.0 127.0 137.0 138.0 137.0 133.0 134.0 140.0 145.0 137.0 134.0 143.0 147.0 141.0 137.0 132.0 131.0 136.0 141.0 135.0 143.0 149.0 150.0 150.0 155.0 160.0 166.0 156.0 152.0 160.0 158.0 154.0 152.0 153.0 154.0 161.0 158.0 154.0 153.0 159.0 154.0 154.0 159.0 155.0 158.0 166.0 161.0 152.0 153.0 161.0 155.0 158.0 160.0 165.0 161.0 163.0 163.0 160.0 165.0 166.0 174.0 171.0 173.0 175.0 169.0 170.0 174.0 174.0 175.0 172.0 171.0 170.0 169.0 172.0 178.0 174.0 179.0 187.0 180.0 181.0 179.0 184.0 181.0 180.0 181.0 178.0 180.0 177.0 187.0 188.0 189.0 191.0 189.0 190.0 183.0 189.0 186.0 193.0 190.0 186.0 192.0 190.0 194.0 195.0 203.0 205.0 208.0 205.0 212.0 215.0 220.0 223.0 222.0 222.0 229.0 230.0 227.0 226.0 225.0 222.0 219.0 222.0 219.0 218.0 219.0 223.0 227.0 222.0 224.0 225.0 230.0 229.0 231.0 230.0 233.0 234.0 236.0 232.0 232.0 226.0 227.0 223.0 220.0 219.0 217.0 217.0 211.0 210.0 204.0 204.0 196.0 195.0 194.0 185.0 187.0 186.0 189.0 183.0 180.0 188.0 178.0 178.0 177.0 175.0 178.0 158.0 169.0 168.0 159.0 160.0 157.0 154.0 151.0 147.0 143.0 146.0 147.0 147.0 149.0 146.0 146.0 152.0 146.0 146.0 150.0 137.0 145.0 146.0 148.0 149.0 143.0 151.0 153.0 152.0 150.0 151.0 153.0 155.0 155.0 155.0 158.0 162.0 167.0 170.0 165.0 170.0 171.0 172.0 173.0 176.0 173.0 177.0 176.0 177.0 173.0 174.0 174.0 177.0 178.0 178.0 175.0 182.0 184.0 185.0 181.0 182.0 184.0 183.0 185.0 189.0 195.0 196.0 194.0 196.0 196.0 195.0 194.0 203.0 197.0 205.0 205.0 204.0 206.0 204.0 209.0 205.0 209.0 208.0 206.0 200.0 199.0 203.0 212.0 206.0 206.0 208.0 215.0 218.0 218.0 216.0 214.0 216.0 214.0 221.0 219.0 221.0 216.0 216.0 217.0 222.0 224.0 226.0 226.0 231.0 231.0 225.0 228.0 231.0 228.0 225.0 225.0 229.0 231.0 241.0 237.0 237.0 236.0 240.0 237.0 244.0 243.0 243.0 236.0 240.0 240.0 242.0 237.0 234.0 229.0 234.0 222.0 236.0 237.0 238.0 236.0 233.0 225.0 233.0 232.0 233.0 234.0 234.0 228.0 229.0 233.0 233.0 235.0 237.0 238.0 240.0 235.0 231.0 230.0 238.0 236.0 237.0 237.0 231.0 232.0 232.0 235.0 239.0 243.0 245.0 243.0 236.0 233.0 235.0 243.0 245.0 239.0 240.0 246.0 242.0 243.0 250.0 240.0 242.0 241.0 243.0 235.0 236.0 239.0 235.0 233.0 244.0 248.0 245.0 248.0 247.0 251.0 251.0 248.0 246.0 247.0 249.0 249.0 254.0 242.0 238.0 239.0 235.0 249.0 247.0 240.0 246.0 254.0 256.0 260.0 258.0 263.0 261.0 264.0 267.0 262.0 259.0 259.0 262.0 263.0 270.0 269.0 267.0 273.0 272.0 271.0 273.0 271.0 269.0 264.0 268.0 270.0 272.0 280.0 277.0 277.0 275.0 278.0 283.0 283.0 280.0 278.0 278.0 272.0 278.0 281.0 276.0 279.0 288.0 288.0 295.0 285.0 290.0 281.0 285.0 287.0 290.0 287.0 292.0 290.0 289.0 298.0 289.0 285.0 287.0 279.0 294.0 289.0 286.0 295.0 295.0 297.0 299.0 295.0 294.0 296.0 293.0 299.0 298.0 299.0 305.0 301.0 297.0 292.0 295.0 298.0 302.0 307.0 313.0 316.0 316.0 316.0 312.0 314.0 300.0 306.0 299.0 308.0 310.0 308.0 310.0 311.0 312.0 321.0 293.0 303.0 293.0 294.0 297.0 293.0 304.0 302.0 295.0 296.0 288.0 294.0 297.0 298.0 306.0 303.0 299.0 282.0 293.0 309.0 309.0 301.0 295.0 294.0 291.0 289.0 290.0 291.0 297.0 293.0 286.0 278.0 278.0 279.0 264.0 245.0 250.0 249.0 252.0 250.0 250.0 253.0 249.0 263.0 271.0 258.0 263.0 262.0 266.0 260.0 260.0 265.0 260.0 258.0 253.0 255.0 249.0 250.0 247.0 247.0 247.0 248.0 248.0 252.0 249.0 249.0 248.0 247.0 252.0 251.0 252.0 256.0 261.0 257.0 260.0 260.0 254.0 258.0 262.0 262.0 259.0 265.0 268.0 269.0 269.0 269.0 269.0 269.0 267.0 273.0 272.0 268.0 266.0 270.0 272.0 273.0 271.0 277.0 274.0 272.0 273.0 274.0 274.0 278.0 284.0 284.0 285.0 290.0 287.0 284.0 277.0 277.0 284.0 291.0 293.0 288.0 285.0 281.0 288.0 288.0 290.0 287.0 282.0 279.0 277.0 277.0 279.0 280.0 282.0 285.0 294.0 290.0 290.0 289.0 293.0 288.0 284.0 287.0 286.0 289.0 292.0 291.0 293.0 296.0 288.0 277.0 291.0 292.0 285.0 289.0 289.0 289.0 293.0 292.0 300.0 285.0 289.0 292.0 293.0 290.0 283.0 287.0 285.0 286.0 283.0 289.0 287.0 284.0 279.0 272.0 276.0 278.0 278.0 279.0 276.0 278.0 274.0 270.0 258.0 274.0 269.0 258.0 260.0 251.0 257.0 263.0 266.0 266.0 267.0 271.0 264.0 276.0 268.0 270.0 270.0 269.0 260.0 265.0 258.0 264.0 269.0 268.0 266.0 271.0 262.0 262.0 247.0 244.0 252.0 254.0 254.0 259.0 256.0 251.0 242.0 233.0 222.0 200.0 196.0 191.0 189.0 184.0 185.0 193.0 196.0 193.0 192.0 194.0 198.0 196.0 197.0 196.0 194.0 195.0 195.0 194.0 201.0 207.0 204.0 202.0 200.0 196.0 197.0 193.0 198.0 201.0 207.0 212.0 226.0 226.0 228.0 219.0 214.0 211.0 214.0 219.0 215.0 216.0 210.0 202.0 202.0 204.0 198.0 194.0 194.0 192.0 203.0 200.0 198.0 197.0 196.0 193.0 194.0 189.0 189.0 188.0 186.0 183.0 185.0 185.0 190.0 189.0 193.0 193.0 192.0 192.0 192.0 196.0 196.0 203.0 198.0 197.0 196.0 197.0 197.0 195.0 197.0 198.0 203.0 204.0 205.0 200.0 201.0 205.0 204.0 209.0 209.0 207.0 208.0 207.0 209.0 214.0 208.0 213.0 213.0 217.0 216.0 214.0 214.0 219.0 214.0 218.0 218.0 218.0 220.0 221.0 224.0 220.0 222.0 224.0 220.0 219.0 223.0 231.0 231.0 231.0 222.0 229.0 235.0 242.0 241.0 239.0 236.0 240.0 233.0 244.0 241.0 238.0 236.0 236.0 239.0 233.0 241.0 241.0 243.0 244.0 244.0 246.0 247.0 240.0 244.0 251.0 248.0 250.0 237.0 243.0 245.0 247.0 249.0 246.0 246.0 248.0 249.0 250.0 252.0 253.0 252.0 250.0 253.0 252.0 247.0 246.0 247.0 246.0 246.0 246.0 244.0 240.0 247.0 246.0 244.0 241.0 239.0 236.0 234.0 236.0 237.0 237.0 236.0 225.0 222.0 221.0 222.0 220.0 220.0 214.0 212.0 211.0 207.0 205.0 205.0 202.0 207.0 201.0 201.0 199.0 194.0 193.0 195.0 195.0 196.0 190.0 193.0 192.0 192.0 193.0 194.0 192.0 191.0 189.0 181.0 184.0 188.0 188.0 190.0 191.0 195.0 195.0 195.0 195.0 197.0 190.0 181.0 187.0 190.0 194.0 200.0 202.0 198.0 200.0 199.0 204.0 202.0 203.0 199.0 201.0 199.0 198.0 205.0 204.0 206.0 204.0 208.0 204.0 210.0 209.0 206.0 206.0 206.0 209.0 206.0 211.0 214.0 213.0 211.0 214.0 215.0 217.0 217.0 217.0 216.0 213.0 218.0 216.0 213.0 219.0 226.0 223.0 223.0 229.0 233.0 237.0 239.0 241.0 239.0 239.0 241.0 239.0 242.0 241.0 238.0 240.0 242.0 241.0 237.0 237.0 241.0 239.0 239.0 240.0 244.0 242.0 247.0 248.0 250.0 240.0 250.0 250.0 242.0 244.0 243.0 242.0 246.0 238.0 240.0 245.0 242.0 242.0 237.0 239.0 240.0 242.0 244.0 244.0 244.0 244.0 242.0 243.0 239.0 239.0 245.0 243.0 249.0 250.0 253.0 252.0 250.0 246.0 250.0 248.0 252.0 250.0 251.0 252.0 252.0 251.0 258.0 257.0 255.0 239.0 239.0 241.0 238.0 233.0 231.0 226.0 233.0 233.0 224.0 220.0 217.0 224.0 225.0 210.0 211.0 211.0 214.0 213.0 220.0 226.0 223.0 218.0 215.0 220.0 215.0 214.0 214.0 230.0 224.0 224.0 224.0 217.0 219.0 218.0 218.0 219.0 208.0 207.0 199.0 190.0 186.0 189.0 191.0 184.0 180.0 184.0 174.0 179.0 171.0 166.0 165.0 167.0 166.0 167.0 169.0 176.0 175.0 183.0 186.0 191.0 187.0 185.0 187.0 185.0 184.0 191.0 197.0 192.0 192.0 194.0 186.0 187.0 187.0 187.0 190.0 193.0 185.0 188.0 190.0 190.0 186.0 190.0 184.0 183.0 164.0 170.0 174.0 175.0 174.0 182.0 183.0 180.0 185.0 181.0 182.0 171.0 170.0 169.0 171.0 168.0 161.0 158.0 159.0 163.0 177.0 176.0 179.0 179.0 165.0 158.0 165.0 171.0 170.0 161.0 165.0 161.0 163.0 164.0 187.0 161.0 159.0 165.0 151.0 147.0 142.0 139.0 127.0 127.0 150.0 149.0 142.0 POLYGON ((35 10, 10 20, 15 40, 45 45, 35 10), (20 30, 35 35, 30 20, 20 30)) 44.586548 5066 Crossing 57.607200 5067 Dot 44.826904 5096 Dot 50.594727 5142 Dot 127.711200 5156 Dot 96.926400 5224 Dot 82.600800 5229 Dot 82.905600 5237 Dot 66.696655 5254 Dot 74.627442 5258 Dot 65.254761 5264 Dot 77.419200 526708 Dot 74.676000 526750 Dot 78.713135 527614 Dot 78.713135 527631 Dot 68.275200 5278 Dot 64.008000 5289 Dot 52.997925 5374FIRE Dot 56.388000 5376 Dot 56.388000 6006 Dot 46.028564 6006BLUE Dot 37.616943 6014MEADOW Dot 56.388000 6029 Dot 50.292000 6053 Dot 25.603200 6066 Dot 34.442400 6067 Dot 30.480000 6071 Dot 15.240000 6073 Dot 37.795200 6084 Dot 64.008000 6130 Dot 64.008000 6131 Dot 62.788800 6153 Dot 55.473600 6171 Dot 62.484000 6176 Dot 62.179200 6177 Dot 69.799200 6272 Dot 73.152000 6272 Dot 70.104000 6278 Dot 57.564209 6280 Dot 66.696655 6283 Dot 72.945191 6289 Dot 72.847200 6297 Dot 53.644800 6328 Dot 43.891200 6354 Dot 48.768000 635722 Dot 49.072800 635783 Dot 62.484000 6373 Dot 3.962400 6634 Dot 13.411200 6979 Dot 34.012085 6997 Dot 87.782400 BEAR HILL BEAR HILL TOWER Tall Tower 23.469600 BELLEVUE BELLEVUE Parking Area 43.384766 6016 Trailhead 89.916000 5236BRIDGE Bridge 55.473600 5376BRIDGE Bridge 52.730400 6181CROSS Crossing 45.110400 6042CROSS Crossing DARKHOLLPO Fishing Area 56.083200 6121DEAD Danger Area 117.043200 5179DEAD Danger Area 69.494400 5299DEAD Danger Area 56.997600 5376DEAD Danger Area 46.939200 6353DEAD Danger Area 61.264800 6155DEAD Danger Area 110.947200 GATE14 Truck Stop 77.724000 GATE16 Truck Stop 65.836800 GATE17 Truck Stop 57.302400 GATE19 Truck Stop 49.377600 GATE21 Truck Stop 81.076800 GATE24 Truck Stop 21.515015 GATE5 Truck Stop 26.561890 GATE6 Trailhead 32.004000 6077LOGS Amusement Park 119.809082 5148NANEPA Trailhead 73.761600 5267OBSTAC Amusement Park 45.307495 PANTHRCAVE Tunnel 77.992066 5252PURPLE Summit 67.970400 5287WATER Swimming Area 81.076800 5239ROAD Truck Stop 67.360800 5278ROAD Truck Stop 53.949600 5058ROAD ROAD CROSSING Dot 69.799200 SHEEPFOLD Parking Area 64.008000 SOAPBOX Cemetery 64.533692 5376STREAM Bridge 61.649902 5144SUMMIT Summit 67.360800 5150TANK WATER TANK Museum BELLEVUE 1 23.469600 BELLEVUE BELLEVUE Parking Area 26.561890 GATE6 Trailhead 45.307495 PANTHRCAVE Tunnel 37.616943 6014MEADOW Dot 56.388000 6006 Dot 46.028564 6006BLUE Dot 44.826904 5096 Dot 44.586548 5066 Crossing 57.607200 5067 Dot 53.949600 5058ROAD ROAD CROSSING Dot 67.360800 5150TANK WATER TANK Museum 50.594727 5142 Dot 61.649902 5144SUMMIT Summit 127.711200 5156 Dot 119.809082 5148NANEPA Trailhead 74.627442 5258 Dot 77.992066 5252PURPLE Summit 78.713135 527631 Dot 78.713135 527614 Dot 73.761600 5267OBSTAC Amusement Park 68.275200 5278 Dot 64.008000 5289 Dot 52.997925 5374FIRE Dot 56.388000 5376 Dot 64.533692 5376STREAM Bridge 53.644800 6328 Dot 48.768000 635722 Dot 49.072800 635783 Dot 62.484000 6373 Dot 87.782400 BEAR HILL BEAR HILL TOWER Tall Tower 72.945191 6289 Dot 72.847200 6297 Dot 66.696655 6283 Dot 57.564209 6280 Dot 62.179200 6177 Dot 62.484000 6176 Dot 62.788800 6153 Dot 55.473600 6171 Dot 64.008000 6131 Dot 64.008000 6130 Dot 56.388000 6029 Dot 56.388000 6006 Dot 37.616943 6014MEADOW Dot 45.307495 PANTHRCAVE Tunnel 26.561890 GATE6 Trailhead 23.469600 BELLEVUE BELLEVUE Parking Area POLYGON((4.8352495472368009 52.3561217600921438,4.8354139113045580 52.3561243429663534,4.8356082266282945 52.3561267417385281,4.8358010085903622 52.3561273083083663,4.8358010085903622 52.3561273083083663,4.8358035242637225 52.3559935212917722,4.8363777606561538 52.3559985348227173,4.8365863082998608 52.3560003600829731,4.8365523717335313 52.3570990145454189,4.8365884597636066 52.3572643433297529,4.8366320506970659 52.3574639095218686,4.8366736405531485 52.3576544056339870,4.8367264446828226 52.3578947700094304,4.8367922739966023 52.3581940807800450,4.8368228816936947 52.3583326871276356,4.8368228816936947 52.3583326871276356,4.8346348012064322 52.3583075969840550,4.8346348012064322 52.3583075969840550,4.8346348010943823 52.3583076059723282,4.8346348010943823 52.3583076059723282,4.8344931735728114 52.3583059732702338,4.8343773230572911 52.3583046496785585,4.8342182417472204 52.3583028092031384,4.8340047277034000 52.3583004442080195,4.8340047277034000 52.3583004442080195,4.8340047286008216 52.3583003723016063,4.8340047286008216 52.3583003723016063,4.8333843154510516 52.3582932434377639,4.8333843154510516 52.3582932434377639,4.8333915914677918 52.3580669388087898,4.8333968982183286 52.3578913129544787,4.8334415565569193 52.3563602568407660,4.8336003450092706 52.3563614767834267,4.8336013166539615 52.3563318721204567,4.8336013166539615 52.3563318721204567,4.8339582156582548 52.3563361223319603,4.8339656498645338 52.3561015845598732,4.8340692910524092 52.3561032110135258,4.8340692910524092 52.3561032110135258,4.8345511248958477 52.3561107854074095,4.8345511248958477 52.3561107854074095,4.8345513450958055 52.3561107864365809,4.8345513450958055 52.3561107864365809,4.8346742584771087 52.3561127181661092,4.8346742584771087 52.3561127181661092,4.8347750227755597 52.3561143035917596,4.8347750227755597 52.3561143035917596,4.8352495472368009 52.3561217600921438)) POLYGON EMPTY CDATA example CDATA Tags are useful!

Text is more readable and easier to write when you can avoid using entity references.

]]>
102.595626,14.996729
MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 45 20, 30 5, 10 10, 10 30, 20 35),(30 20, 20 25, 20 15, 30 20)))42.943 -71.032 500 Simple placemark Attached to the ground. Intelligently places itself at the height of the underlying terrain. 0,0,0 Garmin International Example GPX Document 4.46 4.94 6.87 45.256 -110.45 46.46 -109.48 43.84 -109.86 Mons and its opposite {"type": "FeatureCollection", "features": [{"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75277, 35.42072], [-80.75314, 35.42107], [-80.75352, 35.42159], [-80.75726, 35.42794], [-80.75893, 35.42995], [-80.75928, 35.43016], [-80.75977, 35.43028], [-80.75977, 35.43028], [-80.76035, 35.43043], [-80.7609, 35.43069], [-80.7615, 35.43114], [-80.76222, 35.43187], [-80.76251, 35.43225], [-80.76261, 35.43246], [-80.76266, 35.43278], [-80.76265, 35.43332], [-80.76279, 35.43377], [-80.76308, 35.43413], [-80.76379, 35.43464], [-80.76412, 35.43503], [-80.76434, 35.43565], [-80.76441, 35.4362], [-80.76453, 35.43788], [-80.76452, 35.4382], [-80.76442, 35.43866], [-80.76442, 35.43866], [-80.76392, 35.43969], [-80.76382, 35.4401], [-80.76386, 35.44109], [-80.76401, 35.44217], [-80.76426, 35.44283], [-80.7658, 35.4448], [-80.76597, 35.44519], [-80.76602, 35.44555], [-80.76596, 35.44596], [-80.76568, 35.44642], [-80.76487, 35.44716], [-80.76418, 35.44801], [-80.76401, 35.44829], [-80.76397, 35.44856], [-80.76409, 35.44928], [-80.76409, 35.44955], [-80.76402, 35.45106], [-80.76393, 35.4515], [-80.76371, 35.45188], [-80.76308, 35.45271], [-80.76264, 35.45402], [-80.76246, 35.45445], [-80.76209, 35.4551], [-80.76173, 35.45558], [-80.76115, 35.45618], [-80.75998, 35.45718], [-80.75959, 35.45762], [-80.75939, 35.45793], [-80.75932, 35.45819], [-80.75922, 35.45922], [-80.75855, 35.46121], [-80.75779, 35.46275], [-80.75738, 35.46478], [-80.75633, 35.46824], [-80.75619, 35.46857], [-80.75016, 35.47668], [-80.75016, 35.47668], [-80.75134, 35.47748], [-80.75278, 35.47857], [-80.75522, 35.48055], [-80.75605, 35.48133], [-80.75679, 35.48229], [-80.75731, 35.48316], [-80.76001, 35.48856], [-80.76001, 35.48856], [-80.76471, 35.49024], [-80.76471, 35.49024], [-80.76692, 35.48734], [-80.77014, 35.48202], [-80.77029, 35.48141], [-80.77043, 35.47728], [-80.77051, 35.47695], [-80.77063, 35.47675], [-80.77129, 35.47627], [-80.77338, 35.47499], [-80.77385, 35.47479], [-80.77466, 35.47456], [-80.77496, 35.47451], [-80.77555, 35.47452], [-80.78021, 35.47508], [-80.78021, 35.47508], [-80.78108, 35.47516], [-80.7814, 35.47515], [-80.78169, 35.47508], [-80.78513, 35.47345], [-80.78513, 35.47345], [-80.78478, 35.473], [-80.78408, 35.47125], [-80.78326, 35.46979], [-80.78294, 35.4695], [-80.78236, 35.4692], [-80.78193, 35.46861], [-80.78131, 35.46814], [-80.78114, 35.46795], [-80.77994, 35.46602], [-80.77953, 35.46526], [-80.77923, 35.46491], [-80.77911, 35.46452], [-80.77921, 35.46345], [-80.77921, 35.46345], [-80.77963, 35.4634], [-80.78107, 35.46309], [-80.7814, 35.46306], [-80.78186, 35.46311], [-80.78234, 35.46325], [-80.78308, 35.4635], [-80.78333, 35.46365], [-80.78376, 35.46411], [-80.78394, 35.46449], [-80.78397, 35.46492], [-80.78386, 35.46542], [-80.78318, 35.46637], [-80.783, 35.46685], [-80.78299, 35.46727], [-80.78307, 35.46766], [-80.78319, 35.46798], [-80.78344, 35.4683], [-80.78459, 35.46962], [-80.78509, 35.4701], [-80.7864, 35.47105], [-80.7868, 35.47129], [-80.78719, 35.47141], [-80.78903, 35.47162], [-80.79018, 35.47152], [-80.79018, 35.47152], [-80.78997, 35.47095], [-80.79022, 35.46936], [-80.79017, 35.46844], [-80.79029, 35.46802], [-80.79053, 35.46769], [-80.79178, 35.46668], [-80.79263, 35.4663], [-80.79466, 35.46507], [-80.79547, 35.46466], [-80.79563, 35.46453], [-80.79624, 35.46382], [-80.79659, 35.4636], [-80.79718, 35.46328], [-80.7974, 35.46322], [-80.79791, 35.46318], [-80.79836, 35.46305], [-80.79965, 35.46218], [-80.79989, 35.46207], [-80.80023, 35.46201], [-80.80051, 35.46203], [-80.8019, 35.46246], [-80.80237, 35.46249], [-80.80292, 35.46236], [-80.80452, 35.46168], [-80.80491, 35.46164], [-80.80531, 35.46172], [-80.80872, 35.46366], [-80.80891, 35.46391], [-80.80898, 35.46414], [-80.80904, 35.46524], [-80.80904, 35.46524], [-80.80967, 35.46515], [-80.81048, 35.4648], [-80.81068, 35.46467], [-80.81118, 35.46421], [-80.81118, 35.46421], [-80.80908, 35.46268], [-80.80832, 35.46197], [-80.80762, 35.46112], [-80.80752, 35.46108], [-80.80265, 35.45563], [-80.80251, 35.45539], [-80.80249, 35.4552], [-80.80262, 35.45501], [-80.8028, 35.45496], [-80.8028, 35.45496], [-80.80201, 35.45462], [-80.80131, 35.4542], [-80.7967, 35.4507], [-80.79601, 35.45024], [-80.79601, 35.45024], [-80.79658, 35.4494], [-80.79682, 35.44892], [-80.79849, 35.44498], [-80.79874, 35.44451], [-80.80039, 35.44281], [-80.80083, 35.44253], [-80.80162, 35.44223], [-80.80188, 35.44207], [-80.8023, 35.44173], [-80.80246, 35.4415], [-80.80258, 35.44105], [-80.80255, 35.44072], [-80.80203, 35.43964], [-80.80199, 35.43932], [-80.80209, 35.43903], [-80.80286, 35.43748], [-80.80291, 35.43724], [-80.80293, 35.43653], [-80.80293, 35.43653], [-80.80274, 35.43638], [-80.80167, 35.43512], [-80.7996, 35.43312], [-80.79428, 35.42758], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79201, 35.42529], [-80.79105, 35.42488], [-80.78822, 35.42335], [-80.7879, 35.42324], [-80.7875, 35.42319], [-80.78718, 35.4232], [-80.78176, 35.42352], [-80.78116, 35.42347], [-80.77896, 35.42292], [-80.77821, 35.42269], [-80.77798, 35.4225], [-80.77754, 35.4219], [-80.77713, 35.42156], [-80.77666, 35.42129], [-80.77589, 35.42095], [-80.77551, 35.42084], [-80.77484, 35.42075], [-80.77335, 35.42061], [-80.77282, 35.42064], [-80.77201, 35.42084], [-80.76792, 35.42236], [-80.76747, 35.4226], [-80.76462, 35.42464], [-80.7644, 35.42474], [-80.764, 35.42478], [-80.7631, 35.42467], [-80.76016, 35.42421], [-80.75971, 35.42409], [-80.75839, 35.42362], [-80.75811, 35.42354], [-80.7578, 35.42352], [-80.75713, 35.42358], [-80.75628, 35.42376], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.7534, 35.42141], [-80.75294, 35.42086], [-80.75256, 35.42057], [-80.75208, 35.42031], [-80.75159, 35.42013], [-80.74601, 35.41862], [-80.74558, 35.41847], [-80.74309, 35.41645], [-80.74092, 35.4145], [-80.73984, 35.41359], [-80.73526, 35.41053], [-80.73379, 35.40906], [-80.73343, 35.40876], [-80.73307, 35.40855], [-80.73263, 35.40842], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73068, 35.40803], [-80.73004, 35.40709], [-80.72961, 35.40628], [-80.72908, 35.40393], [-80.7291, 35.40344], [-80.72923, 35.40296], [-80.72939, 35.40268], [-80.72999, 35.40205], [-80.73019, 35.40173], [-80.73088, 35.39884], [-80.73086, 35.39825], [-80.73066, 35.3974], [-80.73051, 35.39643], [-80.73034, 35.39461], [-80.73029, 35.39311]]}, "date": "2011-11-25", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74541, 35.41836], [-80.74572, 35.41852], [-80.74572, 35.41852], [-80.74711, 35.41763], [-80.75152, 35.41452], [-80.75224, 35.41397], [-80.75258, 35.41364], [-80.75295, 35.41311], [-80.75351, 35.41216], [-80.75563, 35.40678], [-80.75634, 35.40556], [-80.75714, 35.40466], [-80.75906, 35.40316], [-80.75998, 35.40249], [-80.76058, 35.40211], [-80.76155, 35.40157], [-80.76386, 35.40047], [-80.76612, 35.39919], [-80.76612, 35.39919], [-80.76939, 35.39733], [-80.77033, 35.39651], [-80.7737, 35.39328], [-80.7745, 35.39267], [-80.77556, 35.39213], [-80.77556, 35.39213], [-80.77503, 35.39166], [-80.77113, 35.38892], [-80.76918, 35.38759], [-80.76869, 35.38736], [-80.7676, 35.38716], [-80.76564, 35.38689], [-80.76521, 35.38686], [-80.76456, 35.3869], [-80.76378, 35.38712], [-80.76102, 35.38827], [-80.76018, 35.38853], [-80.75968, 35.38859], [-80.75884, 35.38856], [-80.75825, 35.38843], [-80.75789, 35.38828], [-80.75704, 35.38776], [-80.75671, 35.38742], [-80.7563, 35.38668], [-80.75565, 35.38518], [-80.75508, 35.38432], [-80.75413, 35.38323], [-80.75399, 35.38279], [-80.75402, 35.38243], [-80.75553, 35.37862], [-80.75573, 35.37832], [-80.75611, 35.37795], [-80.75674, 35.37759], [-80.75735, 35.3774], [-80.75805, 35.37736], [-80.75862, 35.37743], [-80.76162, 35.37853], [-80.76221, 35.37879], [-80.76516, 35.38052], [-80.76552, 35.38068], [-80.76587, 35.38077], [-80.76803, 35.38115], [-80.7685, 35.38117], [-80.76889, 35.3811], [-80.7692, 35.38099], [-80.77031, 35.38049], [-80.77078, 35.3804], [-80.77335, 35.38058], [-80.77364, 35.38058], [-80.77418, 35.38047], [-80.77461, 35.38026], [-80.77485, 35.38007], [-80.77562, 35.37919], [-80.77579, 35.37893], [-80.77588, 35.37864], [-80.7759, 35.37835], [-80.7758, 35.37791], [-80.77493, 35.37651], [-80.77405, 35.37449], [-80.77405, 35.37449], [-80.77076, 35.37531], [-80.77037, 35.37537], [-80.77002, 35.37536], [-80.76961, 35.37529], [-80.76517, 35.37399], [-80.76446, 35.37382], [-80.76381, 35.3737], [-80.76206, 35.37361], [-80.76166, 35.37353], [-80.76125, 35.37338], [-80.7582, 35.37169], [-80.75623, 35.371], [-80.75575, 35.37073], [-80.75496, 35.37006], [-80.75376, 35.36863], [-80.75176, 35.36689], [-80.74977, 35.36557], [-80.74935, 35.36524], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74841, 35.36441], [-80.74807, 35.36734], [-80.74768, 35.36903], [-80.74752, 35.36956], [-80.74734, 35.36994], [-80.74705, 35.37032], [-80.74668, 35.37067], [-80.74567, 35.37137], [-80.74519, 35.37161], [-80.7433, 35.3722], [-80.74276, 35.37251], [-80.74218, 35.37306], [-80.74182, 35.37368], [-80.7397, 35.37996], [-80.7396, 35.38007], [-80.7396, 35.38007], [-80.73754, 35.37989], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.72937, 35.38327], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73029, 35.39311]]}, "date": "2011-11-26", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72977, 35.40227], [-80.73013, 35.40186], [-80.73025, 35.40154], [-80.7309, 35.39864], [-80.73051, 35.39643], [-80.73034, 35.39461], [-80.7302, 35.38882], [-80.72996, 35.38803], [-80.72911, 35.38634], [-80.72899, 35.38591], [-80.72904, 35.38526], [-80.7294, 35.38313], [-80.72947, 35.38294], [-80.72993, 35.38223], [-80.73056, 35.3817], [-80.73168, 35.38118], [-80.73298, 35.38066], [-80.73431, 35.38006], [-80.73496, 35.37958], [-80.73557, 35.3789], [-80.73557, 35.3789], [-80.73695, 35.37964], [-80.73754, 35.37989], [-80.73972, 35.3801], [-80.73972, 35.3801], [-80.7397, 35.37996], [-80.74169, 35.37404], [-80.74194, 35.37342], [-80.74209, 35.37318], [-80.74263, 35.37261], [-80.7431, 35.3723], [-80.74372, 35.37205], [-80.74519, 35.37161], [-80.74567, 35.37137], [-80.74668, 35.37067], [-80.74705, 35.37032], [-80.74734, 35.36994], [-80.74752, 35.36956], [-80.74768, 35.36903], [-80.74807, 35.36734], [-80.74841, 35.36441], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74935, 35.36524], [-80.74977, 35.36557], [-80.75176, 35.36689], [-80.75376, 35.36863], [-80.75496, 35.37006], [-80.75575, 35.37073], [-80.75623, 35.371], [-80.7582, 35.37169], [-80.76125, 35.37338], [-80.76166, 35.37353], [-80.76206, 35.37361], [-80.76381, 35.3737], [-80.76446, 35.37382], [-80.76517, 35.37399], [-80.76961, 35.37529], [-80.77002, 35.37536], [-80.77037, 35.37537], [-80.77076, 35.37531], [-80.77389, 35.37453], [-80.77389, 35.37453], [-80.77456, 35.3761], [-80.77563, 35.37794], [-80.77577, 35.37835], [-80.77567, 35.37886], [-80.77546, 35.37917], [-80.77464, 35.38006], [-80.77419, 35.38031], [-80.77378, 35.38042], [-80.77344, 35.38045], [-80.77097, 35.38027], [-80.77062, 35.38029], [-80.77026, 35.38037], [-80.76917, 35.38089], [-80.76852, 35.38104], [-80.76801, 35.38102], [-80.76618, 35.3807], [-80.76559, 35.38058], [-80.76539, 35.3805], [-80.7643, 35.3799], [-80.76263, 35.37887], [-80.76223, 35.37865], [-80.76161, 35.37839], [-80.75862, 35.37731], [-80.75803, 35.37724], [-80.75731, 35.37728], [-80.75667, 35.37747], [-80.75623, 35.37771], [-80.75575, 35.3781], [-80.75551, 35.37839], [-80.7553, 35.3788], [-80.75405, 35.38193], [-80.75388, 35.38243], [-80.75386, 35.38272], [-80.75393, 35.38307], [-80.75403, 35.38328], [-80.75496, 35.3844], [-80.75552, 35.38525], [-80.75613, 35.38668], [-80.75659, 35.38747], [-80.75693, 35.38783], [-80.75719, 35.38803], [-80.75804, 35.38847], [-80.75844, 35.3886], [-80.759, 35.38869], [-80.75969, 35.38872], [-80.76021, 35.38866], [-80.76084, 35.38849], [-80.76391, 35.38721], [-80.76449, 35.38704], [-80.76489, 35.38699], [-80.76552, 35.38701], [-80.76775, 35.38731], [-80.76864, 35.38747], [-80.76912, 35.3877], [-80.77103, 35.38901], [-80.77491, 35.39178], [-80.77537, 35.39222], [-80.77537, 35.39222], [-80.77556, 35.39213], [-80.77503, 35.39166], [-80.76955, 35.38783], [-80.76918, 35.38759], [-80.76869, 35.38736], [-80.7676, 35.38716], [-80.76564, 35.38689], [-80.76521, 35.38686], [-80.76456, 35.3869], [-80.76378, 35.38712], [-80.76102, 35.38827], [-80.76018, 35.38853], [-80.75968, 35.38859], [-80.75884, 35.38856], [-80.75825, 35.38843], [-80.75789, 35.38828], [-80.75704, 35.38776], [-80.75671, 35.38742], [-80.7563, 35.38668], [-80.75565, 35.38518], [-80.75508, 35.38432], [-80.75413, 35.38323], [-80.75399, 35.38279], [-80.75402, 35.38243], [-80.75553, 35.37862], [-80.75573, 35.37832], [-80.75611, 35.37795], [-80.75674, 35.37759], [-80.75735, 35.3774], [-80.75805, 35.37736], [-80.75862, 35.37743], [-80.76162, 35.37853], [-80.76221, 35.37879], [-80.76516, 35.38052], [-80.76552, 35.38068], [-80.76587, 35.38077], [-80.76803, 35.38115], [-80.7685, 35.38117], [-80.76889, 35.3811], [-80.7692, 35.38099], [-80.77031, 35.38049], [-80.77078, 35.3804], [-80.77335, 35.38058], [-80.77364, 35.38058], [-80.77418, 35.38047], [-80.77461, 35.38026], [-80.77485, 35.38007], [-80.77562, 35.37919], [-80.77579, 35.37893], [-80.77588, 35.37864], [-80.7759, 35.37835], [-80.7758, 35.37791], [-80.77493, 35.37651], [-80.77405, 35.37449], [-80.77405, 35.37449], [-80.77389, 35.37453], [-80.77456, 35.3761], [-80.77563, 35.37794], [-80.77575, 35.37823], [-80.77577, 35.37846], [-80.77571, 35.37875], [-80.77557, 35.37903], [-80.77509, 35.37959], [-80.77464, 35.38006], [-80.77404, 35.38036], [-80.77344, 35.38045], [-80.77097, 35.38027], [-80.77062, 35.38029], [-80.77026, 35.38037], [-80.76917, 35.38089], [-80.76852, 35.38104], [-80.76801, 35.38102], [-80.76618, 35.3807], [-80.76559, 35.38058], [-80.76539, 35.3805], [-80.7643, 35.3799], [-80.76263, 35.37887], [-80.76192, 35.37851], [-80.75888, 35.37738], [-80.75844, 35.37727], [-80.75781, 35.37724], [-80.75731, 35.37728], [-80.75667, 35.37747], [-80.75602, 35.37785], [-80.75562, 35.37824], [-80.7553, 35.3788], [-80.75442, 35.38099], [-80.75442, 35.38099], [-80.75238, 35.38084], [-80.75169, 35.38072], [-80.75118, 35.38047], [-80.75038, 35.37981], [-80.7494, 35.37945], [-80.74884, 35.37936], [-80.7482, 35.37943], [-80.74531, 35.38097], [-80.74512, 35.38101], [-80.74512, 35.38101], [-80.74497, 35.38099], [-80.74487, 35.38112], [-80.74446, 35.38125], [-80.74417, 35.38128], [-80.74362, 35.38123], [-80.74006, 35.38016], [-80.7396, 35.38007], [-80.73766, 35.37992], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.7294, 35.38313], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73034, 35.39461], [-80.73051, 35.39643], [-80.7309, 35.39864], [-80.73019, 35.40173], [-80.73004, 35.40199], [-80.72934, 35.40276]]}, "date": "2011-11-27", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75277, 35.42072], [-80.75314, 35.42107], [-80.75352, 35.42159], [-80.75726, 35.42794], [-80.75893, 35.42995], [-80.75928, 35.43016], [-80.75977, 35.43028], [-80.75977, 35.43028], [-80.76035, 35.43043], [-80.7609, 35.43069], [-80.7615, 35.43114], [-80.76222, 35.43187], [-80.76251, 35.43225], [-80.76261, 35.43246], [-80.76266, 35.43278], [-80.76265, 35.43332], [-80.76279, 35.43377], [-80.76308, 35.43413], [-80.76379, 35.43464], [-80.76412, 35.43503], [-80.76434, 35.43565], [-80.76441, 35.4362], [-80.76453, 35.43788], [-80.76452, 35.4382], [-80.76442, 35.43866], [-80.76442, 35.43866], [-80.76392, 35.43969], [-80.76382, 35.4401], [-80.76386, 35.44109], [-80.76401, 35.44217], [-80.76426, 35.44283], [-80.7658, 35.4448], [-80.76597, 35.44519], [-80.76602, 35.44555], [-80.76596, 35.44596], [-80.76568, 35.44642], [-80.76487, 35.44716], [-80.76418, 35.44801], [-80.76401, 35.44829], [-80.76397, 35.44856], [-80.76409, 35.44928], [-80.76409, 35.44955], [-80.76402, 35.45106], [-80.76393, 35.4515], [-80.76371, 35.45188], [-80.76308, 35.45271], [-80.76264, 35.45402], [-80.76246, 35.45445], [-80.76209, 35.4551], [-80.76173, 35.45558], [-80.76115, 35.45618], [-80.75998, 35.45718], [-80.75959, 35.45762], [-80.75939, 35.45793], [-80.75932, 35.45819], [-80.75922, 35.45922], [-80.75855, 35.46121], [-80.75779, 35.46275], [-80.75738, 35.46478], [-80.75633, 35.46824], [-80.75619, 35.46857], [-80.75016, 35.47668], [-80.75016, 35.47668], [-80.75134, 35.47748], [-80.75278, 35.47857], [-80.75522, 35.48055], [-80.75605, 35.48133], [-80.75679, 35.48229], [-80.75731, 35.48316], [-80.76001, 35.48856], [-80.76001, 35.48856], [-80.76471, 35.49024], [-80.76471, 35.49024], [-80.76692, 35.48734], [-80.77014, 35.48202], [-80.77029, 35.48141], [-80.77043, 35.47728], [-80.77051, 35.47695], [-80.77063, 35.47675], [-80.77129, 35.47627], [-80.77338, 35.47499], [-80.77385, 35.47479], [-80.77466, 35.47456], [-80.77496, 35.47451], [-80.77555, 35.47452], [-80.78021, 35.47508], [-80.78021, 35.47508], [-80.78108, 35.47516], [-80.7814, 35.47515], [-80.78169, 35.47508], [-80.78257, 35.47469], [-80.78695, 35.47255], [-80.78738, 35.47238], [-80.78884, 35.47203], [-80.78955, 35.47199], [-80.79018, 35.47206], [-80.79672, 35.47415], [-80.79748, 35.47426], [-80.7998, 35.47442], [-80.8005, 35.47461], [-80.80099, 35.47483], [-80.80147, 35.47516], [-80.80192, 35.47563], [-80.80515, 35.48001], [-80.80552, 35.48044], [-80.80593, 35.48074], [-80.80689, 35.48129], [-80.80988, 35.48296], [-80.81042, 35.48322], [-80.81114, 35.48342], [-80.81157, 35.48346], [-80.81796, 35.48335], [-80.81835, 35.48338], [-80.81883, 35.48349], [-80.81934, 35.48377], [-80.8196, 35.48397], [-80.8196, 35.48397], [-80.81962, 35.48407], [-80.81972, 35.48414], [-80.81984, 35.48416], [-80.81994, 35.4841], [-80.81997, 35.48401], [-80.81992, 35.4839], [-80.82006, 35.48347], [-80.82004, 35.48142], [-80.81995, 35.47924], [-80.81968, 35.47795], [-80.81916, 35.47674], [-80.8167, 35.47246], [-80.81639, 35.4716], [-80.81604, 35.47019], [-80.81576, 35.46964], [-80.81506, 35.46871], [-80.81487, 35.46838], [-80.81426, 35.46677], [-80.81401, 35.46641], [-80.8136, 35.46595], [-80.8124, 35.46509], [-80.81236, 35.465], [-80.81118, 35.46421], [-80.80908, 35.46268], [-80.8085, 35.46216], [-80.80762, 35.46112], [-80.80752, 35.46108], [-80.80265, 35.45563], [-80.80251, 35.45539], [-80.80249, 35.4552], [-80.80262, 35.45501], [-80.8028, 35.45496], [-80.8028, 35.45496], [-80.80201, 35.45462], [-80.80131, 35.4542], [-80.7967, 35.4507], [-80.79601, 35.45024], [-80.79601, 35.45024], [-80.79658, 35.4494], [-80.79682, 35.44892], [-80.79849, 35.44498], [-80.79874, 35.44451], [-80.80039, 35.44281], [-80.80083, 35.44253], [-80.80162, 35.44223], [-80.80188, 35.44207], [-80.8023, 35.44173], [-80.80246, 35.4415], [-80.80258, 35.44105], [-80.80255, 35.44072], [-80.80203, 35.43964], [-80.80199, 35.43932], [-80.80209, 35.43903], [-80.80286, 35.43748], [-80.80291, 35.43724], [-80.80293, 35.43653], [-80.80293, 35.43653], [-80.80274, 35.43638], [-80.80167, 35.43512], [-80.7996, 35.43312], [-80.79428, 35.42758], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79201, 35.42529], [-80.79105, 35.42488], [-80.78822, 35.42335], [-80.7879, 35.42324], [-80.7875, 35.42319], [-80.78718, 35.4232], [-80.78176, 35.42352], [-80.78116, 35.42347], [-80.77896, 35.42292], [-80.77821, 35.42269], [-80.77798, 35.4225], [-80.77754, 35.4219], [-80.77713, 35.42156], [-80.77666, 35.42129], [-80.77589, 35.42095], [-80.77551, 35.42084], [-80.77484, 35.42075], [-80.77335, 35.42061], [-80.77282, 35.42064], [-80.77201, 35.42084], [-80.76792, 35.42236], [-80.76747, 35.4226], [-80.76462, 35.42464], [-80.7644, 35.42474], [-80.764, 35.42478], [-80.7631, 35.42467], [-80.76016, 35.42421], [-80.75971, 35.42409], [-80.75839, 35.42362], [-80.75811, 35.42354], [-80.7578, 35.42352], [-80.75713, 35.42358], [-80.75628, 35.42376], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.7534, 35.42141], [-80.75294, 35.42086], [-80.75256, 35.42057], [-80.75208, 35.42031], [-80.75159, 35.42013], [-80.74572, 35.41852], [-80.74572, 35.41852], [-80.74502, 35.41897], [-80.74457, 35.41942], [-80.74438, 35.41972], [-80.74357, 35.42151], [-80.74245, 35.42302], [-80.74173, 35.42365], [-80.73997, 35.42449], [-80.73972, 35.4247], [-80.73932, 35.42516], [-80.73816, 35.42668], [-80.73642, 35.42943], [-80.73609, 35.42975], [-80.73579, 35.4299], [-80.7314, 35.43146], [-80.72992, 35.43235], [-80.7293, 35.43265], [-80.72694, 35.43335], [-80.72485, 35.43371], [-80.72485, 35.43371], [-80.7238, 35.43138], [-80.72349, 35.4309], [-80.72299, 35.43049], [-80.72218, 35.43024], [-80.71963, 35.42982], [-80.718, 35.42952], [-80.71766, 35.42941], [-80.71627, 35.42868], [-80.71456, 35.42759], [-80.71387, 35.4272], [-80.71266, 35.42686], [-80.71209, 35.42663], [-80.71151, 35.42623], [-80.71103, 35.42562], [-80.70952, 35.42201], [-80.70926, 35.42133], [-80.70913, 35.42079], [-80.70914, 35.41998], [-80.70977, 35.41247], [-80.70992, 35.41192], [-80.71023, 35.41139], [-80.71147, 35.41031], [-80.7117, 35.41006], [-80.71334, 35.40745], [-80.71334, 35.40745], [-80.7135, 35.4071], [-80.71563, 35.40069], [-80.71869, 35.3908], [-80.72042, 35.3848], [-80.72075, 35.38397], [-80.72167, 35.38264], [-80.72196, 35.38228], [-80.72237, 35.3819], [-80.72297, 35.38152], [-80.7256, 35.38017], [-80.72742, 35.37909], [-80.72851, 35.37852], [-80.72894, 35.37822], [-80.72952, 35.37751], [-80.73011, 35.3763], [-80.73169, 35.37355], [-80.73211, 35.37284], [-80.73264, 35.37214], [-80.73272, 35.37197], [-80.73272, 35.37197], [-80.73378, 35.37062], [-80.73503, 35.36861], [-80.73543, 35.36819], [-80.73698, 35.36709], [-80.74085, 35.36572], [-80.74131, 35.36551], [-80.7414, 35.36542], [-80.74176, 35.36489], [-80.74183, 35.36466], [-80.74181, 35.36362], [-80.7419, 35.36321], [-80.74199, 35.36301], [-80.74285, 35.36188], [-80.74313, 35.36135], [-80.74348, 35.35987], [-80.74348, 35.35987], [-80.74369, 35.35976], [-80.74651, 35.3599], [-80.74949, 35.35954], [-80.74949, 35.35954], [-80.74889, 35.36133], [-80.74846, 35.36343], [-80.74844, 35.36387], [-80.74848, 35.3641], [-80.74935, 35.36524], [-80.74977, 35.36557], [-80.75176, 35.36689], [-80.75376, 35.36863], [-80.75496, 35.37006], [-80.75542, 35.37048], [-80.75591, 35.37084], [-80.75623, 35.371], [-80.7582, 35.37169], [-80.75893, 35.37207], [-80.76066, 35.37308], [-80.7615, 35.37348], [-80.76228, 35.37364], [-80.76381, 35.3737], [-80.76446, 35.37382], [-80.76517, 35.37399], [-80.76961, 35.37529], [-80.7702, 35.37537], [-80.77066, 35.37533], [-80.77389, 35.37453], [-80.77389, 35.37453], [-80.77456, 35.3761], [-80.77563, 35.37794], [-80.77577, 35.37835], [-80.77567, 35.37886], [-80.77546, 35.37917], [-80.77464, 35.38006], [-80.77404, 35.38036], [-80.77344, 35.38045], [-80.77097, 35.38027], [-80.77062, 35.38029], [-80.77026, 35.38037], [-80.76917, 35.38089], [-80.76852, 35.38104], [-80.76801, 35.38102], [-80.76618, 35.3807], [-80.76559, 35.38058], [-80.76539, 35.3805], [-80.7643, 35.3799], [-80.76263, 35.37887], [-80.76192, 35.37851], [-80.75888, 35.37738], [-80.75844, 35.37727], [-80.75781, 35.37724], [-80.75731, 35.37728], [-80.75667, 35.37747], [-80.75602, 35.37785], [-80.75562, 35.37824], [-80.7553, 35.3788], [-80.75442, 35.38099], [-80.75442, 35.38099], [-80.75238, 35.38084], [-80.75169, 35.38072], [-80.75118, 35.38047], [-80.75038, 35.37981], [-80.7494, 35.37945], [-80.74884, 35.37936], [-80.7482, 35.37943], [-80.74531, 35.38097], [-80.74512, 35.38101], [-80.74512, 35.38101], [-80.74497, 35.38099], [-80.74487, 35.38112], [-80.74446, 35.38125], [-80.74417, 35.38128], [-80.74362, 35.38123], [-80.74006, 35.38016], [-80.7396, 35.38007], [-80.73766, 35.37992], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.72937, 35.38327], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73029, 35.39311]]}, "date": "2011-11-28", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72865, 35.40114], [-80.73028, 35.40139], [-80.73028, 35.40139], [-80.7309, 35.39864], [-80.73051, 35.39643], [-80.73034, 35.39461], [-80.7302, 35.38882], [-80.72996, 35.38803], [-80.72911, 35.38634], [-80.72899, 35.38591], [-80.72904, 35.38526], [-80.7294, 35.38313], [-80.72947, 35.38294], [-80.72993, 35.38223], [-80.73056, 35.3817], [-80.73168, 35.38118], [-80.73298, 35.38066], [-80.73431, 35.38006], [-80.73496, 35.37958], [-80.73557, 35.3789], [-80.73557, 35.3789], [-80.73695, 35.37964], [-80.73754, 35.37989], [-80.73972, 35.3801], [-80.73972, 35.3801], [-80.7397, 35.37996], [-80.74169, 35.37404], [-80.74194, 35.37342], [-80.74209, 35.37318], [-80.74263, 35.37261], [-80.7431, 35.3723], [-80.74372, 35.37205], [-80.74519, 35.37161], [-80.74567, 35.37137], [-80.74668, 35.37067], [-80.74705, 35.37032], [-80.74734, 35.36994], [-80.74752, 35.36956], [-80.74768, 35.36903], [-80.74807, 35.36734], [-80.74841, 35.36441], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74935, 35.36524], [-80.74977, 35.36557], [-80.75176, 35.36689], [-80.75376, 35.36863], [-80.75496, 35.37006], [-80.75575, 35.37073], [-80.75623, 35.371], [-80.7582, 35.37169], [-80.76079, 35.37315], [-80.76125, 35.37338], [-80.76186, 35.37358], [-80.76241, 35.37365], [-80.76381, 35.3737], [-80.76413, 35.37375], [-80.76517, 35.37399], [-80.76961, 35.37529], [-80.77002, 35.37536], [-80.77037, 35.37537], [-80.77076, 35.37531], [-80.77405, 35.37449], [-80.77909, 35.37297], [-80.78075, 35.37251], [-80.78121, 35.37247], [-80.7817, 35.37251], [-80.78295, 35.3729], [-80.78339, 35.37296], [-80.78472, 35.37284], [-80.78472, 35.37284], [-80.78513, 35.37529], [-80.78617, 35.38252], [-80.7865, 35.38408], [-80.78659, 35.38439], [-80.78668, 35.38447], [-80.78668, 35.38447], [-80.78686, 35.38424], [-80.78813, 35.383], [-80.7888, 35.38249], [-80.78948, 35.38206], [-80.78991, 35.38187], [-80.79396, 35.38059], [-80.7944, 35.3804], [-80.80313, 35.37445], [-80.80574, 35.37294], [-80.80745, 35.37203], [-80.80745, 35.37203], [-80.8077, 35.37243], [-80.80821, 35.37295], [-80.80839, 35.37338], [-80.80859, 35.37423], [-80.80881, 35.37619], [-80.80872, 35.37813], [-80.80877, 35.37857], [-80.80989, 35.38108], [-80.80999, 35.38137], [-80.80998, 35.38165], [-80.80989, 35.38181], [-80.80968, 35.38193], [-80.80646, 35.38332], [-80.80551, 35.38377], [-80.80485, 35.38417], [-80.80457, 35.3844], [-80.80437, 35.38469], [-80.80425, 35.3853], [-80.8042, 35.38641], [-80.80426, 35.38685], [-80.80463, 35.38838], [-80.80465, 35.38868], [-80.80459, 35.38905], [-80.80431, 35.39016], [-80.80313, 35.39375], [-80.80272, 35.3948], [-80.80234, 35.39557], [-80.80226, 35.39591], [-80.80238, 35.39644], [-80.8035, 35.3993], [-80.80368, 35.40065], [-80.80366, 35.40169], [-80.80349, 35.40201], [-80.80186, 35.40341], [-80.80147, 35.40387], [-80.80129, 35.40417], [-80.80126, 35.40431], [-80.80133, 35.4049], [-80.80179, 35.40653], [-80.80203, 35.40691], [-80.80251, 35.40739], [-80.80282, 35.40789], [-80.8031, 35.40852], [-80.80334, 35.40933], [-80.80356, 35.40982], [-80.80437, 35.41073], [-80.80462, 35.41114], [-80.80469, 35.41138], [-80.80471, 35.41173], [-80.80461, 35.41366], [-80.80461, 35.41366], [-80.8022, 35.41425], [-80.80186, 35.41438], [-80.80089, 35.41512], [-80.80057, 35.41562], [-80.79885, 35.41926], [-80.79863, 35.41961], [-80.79732, 35.42077], [-80.79483, 35.42324], [-80.79406, 35.42406], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79201, 35.42529], [-80.79105, 35.42488], [-80.78822, 35.42335], [-80.7879, 35.42324], [-80.7875, 35.42319], [-80.78718, 35.4232], [-80.78176, 35.42352], [-80.78116, 35.42347], [-80.77896, 35.42292], [-80.77821, 35.42269], [-80.77798, 35.4225], [-80.77754, 35.4219], [-80.77713, 35.42156], [-80.77666, 35.42129], [-80.77589, 35.42095], [-80.77551, 35.42084], [-80.77484, 35.42075], [-80.77335, 35.42061], [-80.77282, 35.42064], [-80.77201, 35.42084], [-80.76792, 35.42236], [-80.76747, 35.4226], [-80.76462, 35.42464], [-80.7644, 35.42474], [-80.764, 35.42478], [-80.7631, 35.42467], [-80.76016, 35.42421], [-80.75971, 35.42409], [-80.75839, 35.42362], [-80.75811, 35.42354], [-80.7578, 35.42352], [-80.75713, 35.42358], [-80.75628, 35.42376], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.7534, 35.42141], [-80.75294, 35.42086], [-80.75256, 35.42057], [-80.75208, 35.42031], [-80.75159, 35.42013], [-80.74572, 35.41852], [-80.74572, 35.41852], [-80.74502, 35.41897], [-80.74457, 35.41942], [-80.74438, 35.41972], [-80.74357, 35.42151], [-80.74245, 35.42302], [-80.74173, 35.42365], [-80.73997, 35.42449], [-80.73972, 35.4247], [-80.73932, 35.42516], [-80.73816, 35.42668], [-80.73642, 35.42943], [-80.73609, 35.42975], [-80.73579, 35.4299], [-80.7314, 35.43146], [-80.72992, 35.43235], [-80.7293, 35.43265], [-80.72694, 35.43335], [-80.72485, 35.43371], [-80.72485, 35.43371], [-80.7238, 35.43138], [-80.72349, 35.4309], [-80.72299, 35.43049], [-80.72218, 35.43024], [-80.71963, 35.42982], [-80.718, 35.42952], [-80.71766, 35.42941], [-80.71627, 35.42868], [-80.71456, 35.42759], [-80.71387, 35.4272], [-80.71266, 35.42686], [-80.71209, 35.42663], [-80.71151, 35.42623], [-80.71103, 35.42562], [-80.70952, 35.42201], [-80.70926, 35.42133], [-80.70913, 35.42079], [-80.70914, 35.41998], [-80.70977, 35.41247], [-80.70992, 35.41192], [-80.71023, 35.41139], [-80.71147, 35.41031], [-80.7117, 35.41006], [-80.71334, 35.40745], [-80.71334, 35.40745], [-80.71716, 35.40911], [-80.71772, 35.4093], [-80.71809, 35.4094], [-80.72187, 35.4098], [-80.7223, 35.40982], [-80.7229, 35.40976], [-80.72802, 35.40826], [-80.72892, 35.40821], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73068, 35.40803], [-80.73004, 35.40709], [-80.72969, 35.40648], [-80.72956, 35.40612], [-80.72945, 35.4055], [-80.72912, 35.40425], [-80.72908, 35.40376], [-80.72918, 35.4031], [-80.72939, 35.40268], [-80.72977, 35.40227]]}, "date": "2011-12-05", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72865, 35.40114], [-80.73028, 35.40139], [-80.73028, 35.40139], [-80.7309, 35.39864], [-80.73051, 35.39643], [-80.73034, 35.39461], [-80.7302, 35.38882], [-80.72996, 35.38803], [-80.72911, 35.38634], [-80.72899, 35.38591], [-80.72904, 35.38526], [-80.7294, 35.38313], [-80.72947, 35.38294], [-80.72993, 35.38223], [-80.73056, 35.3817], [-80.73168, 35.38118], [-80.73298, 35.38066], [-80.73431, 35.38006], [-80.73496, 35.37958], [-80.73557, 35.3789], [-80.73557, 35.3789], [-80.73695, 35.37964], [-80.73754, 35.37989], [-80.73972, 35.3801], [-80.73972, 35.3801], [-80.7397, 35.37996], [-80.74169, 35.37404], [-80.74194, 35.37342], [-80.74209, 35.37318], [-80.74263, 35.37261], [-80.7431, 35.3723], [-80.74372, 35.37205], [-80.74519, 35.37161], [-80.74567, 35.37137], [-80.74668, 35.37067], [-80.74705, 35.37032], [-80.74734, 35.36994], [-80.74752, 35.36956], [-80.74768, 35.36903], [-80.74807, 35.36734], [-80.74841, 35.36441], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74844, 35.36387], [-80.74846, 35.36343], [-80.74872, 35.36212], [-80.74897, 35.36103], [-80.74949, 35.35954], [-80.74949, 35.35954], [-80.74651, 35.3599], [-80.74394, 35.35979], [-80.74343, 35.3597], [-80.74257, 35.35935], [-80.73395, 35.35453], [-80.73341, 35.35438], [-80.73295, 35.35432], [-80.73249, 35.35433], [-80.73092, 35.35464], [-80.73061, 35.35464], [-80.72996, 35.35449], [-80.72947, 35.35421], [-80.72738, 35.35245], [-80.72615, 35.35102], [-80.7253, 35.34995], [-80.7253, 35.34995], [-80.72263, 35.34575], [-80.72117, 35.34356], [-80.72057, 35.34236], [-80.72033, 35.34212], [-80.71889, 35.34098], [-80.71858, 35.34052], [-80.71798, 35.33815], [-80.71758, 35.33695], [-80.7176, 35.33673], [-80.7177, 35.3365], [-80.71817, 35.33597], [-80.71838, 35.33562], [-80.71845, 35.33537], [-80.71838, 35.33489], [-80.71852, 35.33465], [-80.71852, 35.33465], [-80.72037, 35.33397], [-80.72141, 35.33336], [-80.72177, 35.3331], [-80.72177, 35.3331], [-80.72245, 35.33278], [-80.72266, 35.33273], [-80.72301, 35.33275], [-80.72322, 35.33285], [-80.72341, 35.33307], [-80.72346, 35.33329], [-80.72342, 35.3335], [-80.7233, 35.3337], [-80.72313, 35.33386], [-80.72291, 35.33397], [-80.72257, 35.33401], [-80.72209, 35.33388], [-80.72152, 35.3335], [-80.72152, 35.3335], [-80.71983, 35.33134], [-80.71901, 35.3302], [-80.71248, 35.31925], [-80.71116, 35.31696], [-80.71116, 35.31696], [-80.71096, 35.31634], [-80.71088, 35.31591], [-80.71087, 35.31549], [-80.71109, 35.31344], [-80.71108, 35.31306], [-80.71086, 35.31185], [-80.71091, 35.31176], [-80.7111, 35.31163], [-80.71228, 35.31129], [-80.71228, 35.31129], [-80.712, 35.31072], [-80.71115, 35.30926], [-80.711, 35.30908], [-80.71064, 35.30879], [-80.70891, 35.30766], [-80.70843, 35.30727], [-80.70672, 35.30455], [-80.70647, 35.30395], [-80.70466, 35.29854], [-80.7046, 35.29743], [-80.70439, 35.2953], [-80.70439, 35.2953], [-80.70178, 35.29694], [-80.70162, 35.29711], [-80.70146, 35.29746], [-80.70142, 35.29799], [-80.70133, 35.29821], [-80.70103, 35.29845], [-80.69992, 35.29897], [-80.69956, 35.29933], [-80.69942, 35.29955], [-80.69942, 35.29955], [-80.69768, 35.29907], [-80.69768, 35.29907], [-80.69772, 35.29872], [-80.69761, 35.2984], [-80.69736, 35.29815], [-80.69705, 35.29798], [-80.6966, 35.29788], [-80.69625, 35.29786], [-80.69589, 35.29795], [-80.69553, 35.29815], [-80.69435, 35.29954], [-80.69405, 35.30003], [-80.69373, 35.30121], [-80.69335, 35.30169], [-80.6932, 35.302], [-80.69319, 35.30222], [-80.69335, 35.30292], [-80.69327, 35.30324], [-80.6931, 35.30345], [-80.6931, 35.30345], [-80.69278, 35.30326], [-80.69244, 35.30324], [-80.69145, 35.3036], [-80.69108, 35.30367], [-80.69058, 35.30368], [-80.68956, 35.30348], [-80.68903, 35.30327], [-80.68872, 35.30301], [-80.68847, 35.30261], [-80.68842, 35.30223], [-80.68842, 35.30223], [-80.68869, 35.30223], [-80.68906, 35.30212], [-80.68972, 35.30163], [-80.68972, 35.30163], [-80.68957, 35.30146], [-80.68898, 35.30104], [-80.6888, 35.30084], [-80.68866, 35.30053], [-80.68864, 35.30026], [-80.68873, 35.3], [-80.68901, 35.29964], [-80.68916, 35.29957], [-80.68916, 35.29944], [-80.68911, 35.29939], [-80.68937, 35.29877], [-80.69001, 35.29773], [-80.69005, 35.29737], [-80.68999, 35.29717], [-80.68978, 35.29688], [-80.68819, 35.29571], [-80.68799, 35.29551], [-80.68766, 35.29461], [-80.68732, 35.29419], [-80.68546, 35.29311], [-80.68546, 35.29311], [-80.68255, 35.29656], [-80.681, 35.29826], [-80.68008, 35.2994], [-80.67923, 35.30026], [-80.67843, 35.30089], [-80.67814, 35.30118], [-80.67814, 35.30118], [-80.67302, 35.30539], [-80.67302, 35.30539], [-80.67353, 35.30751], [-80.67373, 35.30856], [-80.67373, 35.30888], [-80.67358, 35.30969], [-80.67334, 35.31044], [-80.67246, 35.31256], [-80.67234, 35.3133], [-80.67239, 35.31422], [-80.67254, 35.31458], [-80.67306, 35.31547], [-80.67364, 35.31621], [-80.67408, 35.31716], [-80.67453, 35.31767], [-80.67453, 35.31767], [-80.67244, 35.31903], [-80.67118, 35.31978], [-80.67044, 35.32008], [-80.66982, 35.32026], [-80.66894, 35.32042], [-80.6681, 35.32051], [-80.66052, 35.32117], [-80.66052, 35.32117], [-80.66116, 35.32268], [-80.66438, 35.32764], [-80.66565, 35.32994], [-80.66579, 35.33025], [-80.66643, 35.33223], [-80.66673, 35.33277], [-80.66703, 35.33304], [-80.67149, 35.33625], [-80.67468, 35.33883], [-80.67704, 35.34029], [-80.67751, 35.3407], [-80.68082, 35.34395], [-80.68082, 35.34395], [-80.68137, 35.34442], [-80.68191, 35.34474], [-80.68299, 35.34515], [-80.68547, 35.34585], [-80.68633, 35.34627], [-80.68668, 35.3465], [-80.68714, 35.34692], [-80.68876, 35.34861], [-80.68941, 35.34907], [-80.68941, 35.34907], [-80.69315, 35.35055], [-80.69365, 35.35071], [-80.69413, 35.35077], [-80.6944, 35.35078], [-80.69584, 35.35065], [-80.70301, 35.34992], [-80.70336, 35.34991], [-80.70377, 35.34998], [-80.70377, 35.34998], [-80.70433, 35.35016], [-80.70461, 35.35031], [-80.70826, 35.35281], [-80.70866, 35.35303], [-80.70913, 35.35319], [-80.70941, 35.35324], [-80.71209, 35.35342], [-80.71209, 35.35342], [-80.71277, 35.3533], [-80.71321, 35.35306], [-80.71346, 35.35281], [-80.71419, 35.35182], [-80.71445, 35.35155], [-80.71499, 35.3512], [-80.71562, 35.35099], [-80.71624, 35.35091], [-80.72426, 35.35026], [-80.72502, 35.35008], [-80.7253, 35.34995], [-80.7253, 35.34995], [-80.72615, 35.35102], [-80.72738, 35.35245], [-80.72947, 35.35421], [-80.72996, 35.35449], [-80.73061, 35.35464], [-80.73092, 35.35464], [-80.73249, 35.35433], [-80.73295, 35.35432], [-80.73341, 35.35438], [-80.73395, 35.35453], [-80.74257, 35.35935], [-80.74343, 35.3597], [-80.74394, 35.35979], [-80.74651, 35.3599], [-80.74949, 35.35954], [-80.74949, 35.35954], [-80.74897, 35.36103], [-80.74872, 35.36212], [-80.74846, 35.36343], [-80.74844, 35.36387], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74841, 35.36441], [-80.74807, 35.36734], [-80.74768, 35.36903], [-80.74752, 35.36956], [-80.74734, 35.36994], [-80.74705, 35.37032], [-80.74668, 35.37067], [-80.74567, 35.37137], [-80.74519, 35.37161], [-80.7433, 35.3722], [-80.74276, 35.37251], [-80.74218, 35.37306], [-80.74182, 35.37368], [-80.7397, 35.37996], [-80.7396, 35.38007], [-80.7396, 35.38007], [-80.73754, 35.37989], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.7294, 35.38313], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73034, 35.39461], [-80.73051, 35.39643], [-80.7309, 35.39864], [-80.73025, 35.40154], [-80.73013, 35.40186], [-80.72977, 35.40227]]}, "date": "2011-12-06", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72939, 35.40268], [-80.72923, 35.40296], [-80.72912, 35.40332], [-80.72908, 35.40393], [-80.72956, 35.40612], [-80.72976, 35.40661], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.72892, 35.40821], [-80.72802, 35.40826], [-80.7229, 35.40976], [-80.7223, 35.40982], [-80.72187, 35.4098], [-80.71809, 35.4094], [-80.71772, 35.4093], [-80.71716, 35.40911], [-80.71334, 35.40745], [-80.71334, 35.40745], [-80.7117, 35.41006], [-80.71147, 35.41031], [-80.71023, 35.41139], [-80.70992, 35.41192], [-80.70977, 35.41247], [-80.70914, 35.41998], [-80.70913, 35.42079], [-80.70926, 35.42133], [-80.70952, 35.42201], [-80.71103, 35.42562], [-80.71151, 35.42623], [-80.71209, 35.42663], [-80.71266, 35.42686], [-80.71387, 35.4272], [-80.71456, 35.42759], [-80.71627, 35.42868], [-80.71766, 35.42941], [-80.718, 35.42952], [-80.71963, 35.42982], [-80.72218, 35.43024], [-80.72299, 35.43049], [-80.72329, 35.4307], [-80.72349, 35.4309], [-80.7238, 35.43138], [-80.72696, 35.4384], [-80.72724, 35.43916], [-80.72749, 35.4402], [-80.72749, 35.4402], [-80.71359, 35.4384], [-80.70981, 35.43753], [-80.7088, 35.43736], [-80.70755, 35.43728], [-80.70671, 35.43728], [-80.70461, 35.43741], [-80.7034, 35.43738], [-80.70253, 35.43725], [-80.69988, 35.43674], [-80.69885, 35.43666], [-80.69806, 35.43672], [-80.69323, 35.43738], [-80.69246, 35.43741], [-80.6805, 35.43642], [-80.6805, 35.43642], [-80.68038, 35.43754], [-80.68024, 35.43811], [-80.67983, 35.43935], [-80.67926, 35.44075], [-80.67881, 35.44167], [-80.67792, 35.44315], [-80.67648, 35.44492], [-80.6748, 35.44654], [-80.66977, 35.45119], [-80.66677, 35.45405], [-80.66677, 35.45405], [-80.66573, 35.45499], [-80.66558, 35.45507], [-80.66499, 35.45564], [-80.66445, 35.45622], [-80.66411, 35.45673], [-80.66375, 35.45761], [-80.66363, 35.45817], [-80.6636, 35.45874], [-80.66364, 35.45928], [-80.66379, 35.45991], [-80.66401, 35.46045], [-80.66442, 35.46113], [-80.66788, 35.46542], [-80.66973, 35.46783], [-80.66997, 35.46835], [-80.67019, 35.46912], [-80.67037, 35.47146], [-80.67057, 35.47234], [-80.67256, 35.47644], [-80.67285, 35.47734], [-80.67373, 35.48099], [-80.67382, 35.48148], [-80.67384, 35.48234], [-80.67375, 35.48282], [-80.67347, 35.48369], [-80.67164, 35.48877], [-80.67139, 35.48993], [-80.67139, 35.48993], [-80.67561, 35.49098], [-80.67663, 35.49108], [-80.67733, 35.49105], [-80.67789, 35.49096], [-80.67789, 35.49096], [-80.67794, 35.49136], [-80.67797, 35.494], [-80.67802, 35.4946], [-80.67829, 35.49577], [-80.67858, 35.49657], [-80.67903, 35.49702], [-80.67965, 35.49748], [-80.67999, 35.49766], [-80.68022, 35.49774], [-80.68105, 35.49786], [-80.68243, 35.49797], [-80.68286, 35.49815], [-80.68315, 35.49846], [-80.6834, 35.49892], [-80.68404, 35.50083], [-80.68501, 35.50429], [-80.68501, 35.50429], [-80.69014, 35.50283], [-80.69414, 35.50191], [-80.69601, 35.50156], [-80.69669, 35.50147], [-80.69739, 35.50146], [-80.69807, 35.5016], [-80.69934, 35.50197], [-80.69995, 35.50199], [-80.70289, 35.50136], [-80.70328, 35.50121], [-80.704, 35.50081], [-80.70434, 35.50056], [-80.70454, 35.50027], [-80.70454, 35.49994], [-80.70442, 35.49917], [-80.70446, 35.49884], [-80.70537, 35.49572], [-80.70589, 35.49429], [-80.70653, 35.49275], [-80.70665, 35.49214], [-80.7068, 35.49082], [-80.7068, 35.49082], [-80.70834, 35.49153], [-80.71021, 35.49201], [-80.71062, 35.49218], [-80.71106, 35.49243], [-80.71181, 35.49295], [-80.71295, 35.49401], [-80.71562, 35.49632], [-80.716, 35.49678], [-80.71679, 35.49806], [-80.71715, 35.49891], [-80.71823, 35.50189], [-80.7189, 35.50307], [-80.71913, 35.50331], [-80.71963, 35.50357], [-80.72089, 35.50378], [-80.72089, 35.50378], [-80.72087, 35.50687], [-80.72087, 35.50687], [-80.72227, 35.50708], [-80.72293, 35.50702], [-80.72345, 35.50684], [-80.72601, 35.50576], [-80.73035, 35.50441], [-80.73084, 35.5043], [-80.73131, 35.50429], [-80.73284, 35.50443], [-80.73408, 35.50447], [-80.73505, 35.50441], [-80.73578, 35.50431], [-80.73623, 35.50409], [-80.73746, 35.50308], [-80.73779, 35.50296], [-80.7382, 35.50292], [-80.73984, 35.50309], [-80.74343, 35.50363], [-80.74658, 35.50398], [-80.7485, 35.50408], [-80.75495, 35.50421], [-80.75676, 35.50429], [-80.75921, 35.50449], [-80.76221, 35.50463], [-80.76363, 35.50491], [-80.76515, 35.50473], [-80.76515, 35.50473], [-80.76604, 35.50536], [-80.76665, 35.50564], [-80.76696, 35.50572], [-80.76784, 35.50572], [-80.76828, 35.50577], [-80.76828, 35.50577], [-80.76962, 35.49962], [-80.76987, 35.49827], [-80.76988, 35.49772], [-80.76978, 35.49696], [-80.76945, 35.49601], [-80.7671, 35.49226], [-80.76651, 35.49136], [-80.7662, 35.491], [-80.76572, 35.49067], [-80.76517, 35.49041], [-80.76001, 35.48856], [-80.76001, 35.48856], [-80.75731, 35.48316], [-80.75679, 35.48229], [-80.75605, 35.48133], [-80.75522, 35.48055], [-80.75278, 35.47857], [-80.75134, 35.47748], [-80.75016, 35.47668], [-80.75016, 35.47668], [-80.75619, 35.46857], [-80.75633, 35.46824], [-80.75738, 35.46478], [-80.75779, 35.46275], [-80.75855, 35.46121], [-80.75922, 35.45922], [-80.75932, 35.45819], [-80.75939, 35.45793], [-80.75959, 35.45762], [-80.75998, 35.45718], [-80.76115, 35.45618], [-80.76173, 35.45558], [-80.76209, 35.4551], [-80.76246, 35.45445], [-80.76264, 35.45402], [-80.76308, 35.45271], [-80.76371, 35.45188], [-80.76393, 35.4515], [-80.76402, 35.45106], [-80.76409, 35.44955], [-80.76409, 35.44928], [-80.76397, 35.44856], [-80.76401, 35.44829], [-80.76418, 35.44801], [-80.76487, 35.44716], [-80.76568, 35.44642], [-80.76596, 35.44596], [-80.76602, 35.44555], [-80.76597, 35.44519], [-80.7658, 35.4448], [-80.76426, 35.44283], [-80.76401, 35.44217], [-80.76386, 35.44109], [-80.76382, 35.4401], [-80.76392, 35.43969], [-80.76442, 35.43866], [-80.76442, 35.43866], [-80.76452, 35.4382], [-80.76453, 35.43788], [-80.76441, 35.4362], [-80.76434, 35.43565], [-80.76412, 35.43503], [-80.76379, 35.43464], [-80.76308, 35.43413], [-80.76279, 35.43377], [-80.76265, 35.43332], [-80.76266, 35.43278], [-80.76261, 35.43246], [-80.76251, 35.43225], [-80.76222, 35.43187], [-80.7615, 35.43114], [-80.7609, 35.43069], [-80.76035, 35.43043], [-80.75977, 35.43028], [-80.75977, 35.43028], [-80.75928, 35.43016], [-80.75893, 35.42995], [-80.75726, 35.42794], [-80.75352, 35.42159], [-80.75314, 35.42107], [-80.75277, 35.42072], [-80.75208, 35.42031], [-80.75159, 35.42013], [-80.74601, 35.41862], [-80.74558, 35.41847], [-80.74309, 35.41645], [-80.74092, 35.4145], [-80.73984, 35.41359], [-80.73526, 35.41053], [-80.73379, 35.40906], [-80.73343, 35.40876], [-80.73307, 35.40855], [-80.73263, 35.40842], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73068, 35.40803], [-80.73004, 35.40709], [-80.72969, 35.40648], [-80.72956, 35.40612], [-80.72945, 35.4055], [-80.72912, 35.40425], [-80.72908, 35.40393], [-80.72912, 35.40332], [-80.72923, 35.40296], [-80.72939, 35.40268], [-80.72958, 35.40247]]}, "date": "2011-12-08", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.61108, 35.51312], [-80.61268, 35.51252], [-80.61346, 35.51237], [-80.61346, 35.51237], [-80.61369, 35.51213], [-80.61746, 35.50617], [-80.62303, 35.49761], [-80.62303, 35.49761], [-80.62702, 35.49945], [-80.62751, 35.49996], [-80.62757, 35.5001], [-80.62757, 35.5001], [-80.63027, 35.49903], [-80.63027, 35.49903], [-80.63089, 35.50023], [-80.63105, 35.50089], [-80.63105, 35.50089], [-80.63139, 35.50088], [-80.63161, 35.50093], [-80.6402, 35.50549], [-80.64192, 35.50624], [-80.64902, 35.50895], [-80.64982, 35.50932], [-80.65084, 35.50996], [-80.65399, 35.51179], [-80.65515, 35.51209], [-80.65608, 35.51224], [-80.65654, 35.51246], [-80.65852, 35.51365], [-80.65893, 35.51384], [-80.65967, 35.51407], [-80.66081, 35.51434], [-80.67024, 35.51649], [-80.67024, 35.51649], [-80.67001, 35.51785], [-80.66988, 35.519], [-80.66981, 35.52147], [-80.66919, 35.52677], [-80.66907, 35.52717], [-80.66788, 35.52942], [-80.66628, 35.53213], [-80.66571, 35.53426], [-80.66571, 35.53426], [-80.67067, 35.53431], [-80.67216, 35.53437], [-80.6729, 35.53448], [-80.68049, 35.53753], [-80.68148, 35.53787], [-80.68192, 35.53798], [-80.6827, 35.53807], [-80.6827, 35.53807], [-80.68488, 35.53821], [-80.68685, 35.53827], [-80.68902, 35.53821], [-80.68945, 35.53824], [-80.68982, 35.53832], [-80.69236, 35.53933], [-80.69276, 35.53959], [-80.6932, 35.54001], [-80.69368, 35.54081], [-80.69418, 35.5412], [-80.69875, 35.54386], [-80.69914, 35.54417], [-80.70018, 35.54531], [-80.70117, 35.5466], [-80.70117, 35.5466], [-80.70114, 35.54699], [-80.70092, 35.5477], [-80.69996, 35.54823], [-80.69926, 35.54877], [-80.69885, 35.54916], [-80.69842, 35.54969], [-80.69796, 35.55039], [-80.69719, 35.55122], [-80.69624, 35.55216], [-80.69406, 35.55412], [-80.69357, 35.55478], [-80.6932, 35.55545], [-80.69223, 35.55785], [-80.69154, 35.55874], [-80.69083, 35.55942], [-80.69083, 35.55942], [-80.69149, 35.55965], [-80.69211, 35.55971], [-80.69654, 35.55974], [-80.69654, 35.55974], [-80.69659, 35.55987], [-80.69658, 35.56108], [-80.69729, 35.563], [-80.69746, 35.56329], [-80.69858, 35.56424], [-80.69858, 35.56424], [-80.69746, 35.56329], [-80.69729, 35.563], [-80.69658, 35.56108], [-80.69659, 35.55987], [-80.69654, 35.55974], [-80.69654, 35.55974], [-80.69211, 35.55971], [-80.69149, 35.55965], [-80.69083, 35.55942], [-80.69083, 35.55942], [-80.68902, 35.56071], [-80.68776, 35.56187], [-80.68718, 35.56256], [-80.68623, 35.56421], [-80.68359, 35.56943], [-80.68304, 35.57015], [-80.68093, 35.57231], [-80.68076, 35.57254], [-80.68076, 35.57254], [-80.68157, 35.57236], [-80.68263, 35.57228], [-80.68429, 35.57248], [-80.68559, 35.57281], [-80.68629, 35.57312], [-80.68716, 35.5737], [-80.69086, 35.5764], [-80.69162, 35.57676], [-80.69229, 35.57702], [-80.69269, 35.57712], [-80.69352, 35.57725], [-80.69883, 35.57774], [-80.70109, 35.57751], [-80.70685, 35.57629], [-80.70893, 35.57589], [-80.70966, 35.57591], [-80.71041, 35.57615], [-80.7108, 35.57635], [-80.71704, 35.58018], [-80.71936, 35.58104], [-80.7222, 35.58199], [-80.72284, 35.58211], [-80.72317, 35.58213], [-80.72421, 35.58209], [-80.72734, 35.58147], [-80.72897, 35.58125], [-80.73068, 35.58124], [-80.73347, 35.58154], [-80.73347, 35.58154], [-80.7321, 35.56811], [-80.73211, 35.56757], [-80.73217, 35.56724], [-80.73231, 35.56685], [-80.73272, 35.5663], [-80.73321, 35.5659], [-80.73563, 35.56432], [-80.73767, 35.56307], [-80.73805, 35.56272], [-80.73833, 35.56233], [-80.73849, 35.56199], [-80.73879, 35.5608], [-80.739, 35.56038], [-80.74162, 35.55684], [-80.74189, 35.55636], [-80.74205, 35.55566], [-80.7422, 35.55364], [-80.7422, 35.55364], [-80.7423, 35.55285], [-80.74241, 35.55254], [-80.74282, 35.55188], [-80.74379, 35.55069], [-80.74407, 35.55018], [-80.74421, 35.5497], [-80.7442, 35.54932], [-80.74394, 35.54779], [-80.74379, 35.54489], [-80.74379, 35.54489], [-80.74765, 35.54721], [-80.74813, 35.54739], [-80.75046, 35.54805], [-80.75083, 35.5481], [-80.7511, 35.54808], [-80.75297, 35.54777], [-80.75395, 35.5477], [-80.75462, 35.54776], [-80.75537, 35.54796], [-80.75608, 35.54826], [-80.76115, 35.55096], [-80.76207, 35.55153], [-80.76227, 35.55173], [-80.763, 35.5529], [-80.7634, 35.55342], [-80.7657, 35.55535], [-80.76684, 35.55664], [-80.76711, 35.55685], [-80.76758, 35.55701], [-80.77214, 35.55789], [-80.77266, 35.55811], [-80.77299, 35.55836], [-80.77434, 35.55981], [-80.77467, 35.56031], [-80.77478, 35.56066], [-80.77489, 35.56146], [-80.775, 35.56655], [-80.77507, 35.56701], [-80.77526, 35.56763], [-80.77551, 35.5681], [-80.77714, 35.57029], [-80.77769, 35.57117], [-80.77889, 35.57347], [-80.78043, 35.57652], [-80.78129, 35.57849], [-80.78193, 35.57963], [-80.78329, 35.58226], [-80.78368, 35.58322], [-80.7842, 35.58503], [-80.78454, 35.58546], [-80.7848, 35.58566], [-80.78548, 35.58599], [-80.7867, 35.58643], [-80.78726, 35.58671], [-80.78935, 35.58824], [-80.78982, 35.58849], [-80.79032, 35.58868], [-80.79085, 35.58897], [-80.79273, 35.59025], [-80.7928, 35.59034], [-80.7928, 35.59034], [-80.79491, 35.58954], [-80.7992, 35.58731], [-80.7992, 35.58731], [-80.79851, 35.58604], [-80.79851, 35.58604], [-80.79931, 35.58581], [-80.80023, 35.58562], [-80.80023, 35.58562], [-80.79939, 35.58256], [-80.79939, 35.58256], [-80.79898, 35.58252], [-80.79878, 35.58243], [-80.79655, 35.58109], [-80.79535, 35.58006], [-80.79389, 35.57864], [-80.79309, 35.5778], [-80.79237, 35.57672], [-80.79197, 35.57572], [-80.79177, 35.57497], [-80.79168, 35.57388], [-80.79175, 35.57296], [-80.79229, 35.57045], [-80.79233, 35.56955], [-80.79219, 35.56862], [-80.79103, 35.56554], [-80.79072, 35.5649], [-80.78834, 35.56102], [-80.78695, 35.55953], [-80.78388, 35.55656], [-80.7829, 35.55568], [-80.78226, 35.5552], [-80.78173, 35.55487], [-80.78088, 35.55443], [-80.77788, 35.55322], [-80.77645, 35.55256], [-80.77546, 35.55188], [-80.77457, 35.55106], [-80.77382, 35.55017], [-80.76846, 35.54192], [-80.76801, 35.54077], [-80.76794, 35.54013], [-80.76796, 35.53937], [-80.76812, 35.53864], [-80.76896, 35.53639], [-80.76915, 35.53565], [-80.76925, 35.53484], [-80.76927, 35.53417], [-80.76901, 35.53136], [-80.76905, 35.53016], [-80.76913, 35.52918], [-80.77039, 35.52119], [-80.77048, 35.52044], [-80.77045, 35.51942], [-80.77045, 35.51942], [-80.7731, 35.52008], [-80.77382, 35.5203], [-80.77428, 35.52051], [-80.77786, 35.52337], [-80.7792, 35.52425], [-80.77968, 35.52442], [-80.78004, 35.52447], [-80.78034, 35.52445], [-80.78286, 35.52368], [-80.78437, 35.52366], [-80.78626, 35.52399], [-80.7882, 35.52399], [-80.78861, 35.52407], [-80.78897, 35.52425], [-80.79036, 35.52578], [-80.79076, 35.5261], [-80.79136, 35.5264], [-80.79164, 35.52649], [-80.79197, 35.52656], [-80.79268, 35.52662], [-80.79566, 35.52631], [-80.79727, 35.52634], [-80.79746, 35.52639], [-80.79746, 35.52639], [-80.79915, 35.52421], [-80.79942, 35.5237], [-80.79945, 35.52308], [-80.7993, 35.52268], [-80.79874, 35.52158], [-80.79863, 35.5211], [-80.79866, 35.52057], [-80.79866, 35.52057], [-80.80111, 35.52034], [-80.80711, 35.52002], [-80.8078, 35.51977], [-80.80894, 35.51884], [-80.80924, 35.51866], [-80.80949, 35.51854], [-80.80985, 35.51846], [-80.81037, 35.51844], [-80.81077, 35.51851], [-80.814, 35.51941], [-80.8143, 35.51948], [-80.81478, 35.51951], [-80.81986, 35.51813], [-80.82627, 35.51662], [-80.82694, 35.51659], [-80.82967, 35.51714], [-80.83677, 35.51741], [-80.83728, 35.5173], [-80.83762, 35.51709], [-80.83843, 35.51595], [-80.83883, 35.51579], [-80.83953, 35.51566], [-80.83953, 35.51566], [-80.8386, 35.51319], [-80.83836, 35.51223], [-80.83836, 35.51177], [-80.83844, 35.51132], [-80.83878, 35.5105], [-80.83938, 35.50969], [-80.8399, 35.50919], [-80.84038, 35.50888], [-80.84277, 35.50767], [-80.84334, 35.50722], [-80.84585, 35.50488], [-80.84621, 35.50434], [-80.84677, 35.50319], [-80.84848, 35.49926], [-80.84848, 35.49926], [-80.84537, 35.49862], [-80.84505, 35.49851], [-80.84426, 35.498], [-80.84174, 35.49611], [-80.84151, 35.49599], [-80.83693, 35.49456], [-80.83665, 35.49444], [-80.82061, 35.48452], [-80.81997, 35.48405], [-80.81997, 35.48405], [-80.81992, 35.4839], [-80.82006, 35.48347], [-80.81999, 35.47998], [-80.81993, 35.47907], [-80.81968, 35.47795], [-80.81916, 35.47674], [-80.8167, 35.47246], [-80.81639, 35.4716], [-80.81604, 35.47019], [-80.81576, 35.46964], [-80.81506, 35.46871], [-80.81487, 35.46838], [-80.81437, 35.46702], [-80.81418, 35.46665], [-80.81418, 35.46665], [-80.81522, 35.46547], [-80.81566, 35.4651], [-80.81613, 35.46482], [-80.81719, 35.46437], [-80.81764, 35.46405], [-80.81791, 35.46372], [-80.81807, 35.46337], [-80.81849, 35.46206], [-80.81887, 35.46152], [-80.81938, 35.46116], [-80.82021, 35.46069], [-80.82068, 35.46043], [-80.82105, 35.4603], [-80.82105, 35.4603], [-80.82082, 35.4592], [-80.8208, 35.4589], [-80.82114, 35.45733], [-80.82114, 35.45733], [-80.82191, 35.45736], [-80.82235, 35.45729], [-80.82292, 35.4571], [-80.82346, 35.45677], [-80.8248, 35.45557], [-80.82569, 35.45508], [-80.82569, 35.45508], [-80.82519, 35.45428], [-80.82478, 35.45344], [-80.82468, 35.4531], [-80.82448, 35.45284], [-80.82427, 35.45275], [-80.82243, 35.45225], [-80.82106, 35.45175], [-80.82032, 35.45139], [-80.81947, 35.45075], [-80.81947, 35.45075], [-80.80954, 35.45441], [-80.80954, 35.45441], [-80.80916, 35.4537], [-80.80885, 35.45328], [-80.80874, 35.45302], [-80.80875, 35.45268], [-80.80903, 35.45212], [-80.8091, 35.4517], [-80.80901, 35.45134], [-80.80835, 35.44964], [-80.8083, 35.44934], [-80.80843, 35.44883], [-80.8092, 35.44677], [-80.80935, 35.44614], [-80.80933, 35.44579], [-80.80882, 35.4434], [-80.80893, 35.44234], [-80.80915, 35.44185], [-80.80997, 35.44059], [-80.81098, 35.43885], [-80.81108, 35.43807], [-80.81127, 35.43767], [-80.81266, 35.43567], [-80.81266, 35.43567], [-80.81239, 35.43564], [-80.80952, 35.43566], [-80.80873, 35.43561], [-80.80834, 35.43552], [-80.80809, 35.43541], [-80.80764, 35.43509], [-80.80696, 35.43446], [-80.8058, 35.43394], [-80.80532, 35.43386], [-80.80441, 35.43388], [-80.80358, 35.43395], [-80.80358, 35.43395], [-80.80308, 35.43501], [-80.80302, 35.43527], [-80.80293, 35.43653], [-80.80293, 35.43653], [-80.80274, 35.43638], [-80.80167, 35.43512], [-80.7996, 35.43312], [-80.79428, 35.42758], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79201, 35.42529], [-80.79105, 35.42488], [-80.78822, 35.42335], [-80.7879, 35.42324], [-80.7875, 35.42319], [-80.78718, 35.4232], [-80.78176, 35.42352], [-80.78116, 35.42347], [-80.77896, 35.42292], [-80.77821, 35.42269], [-80.77798, 35.4225], [-80.77754, 35.4219], [-80.77713, 35.42156], [-80.77666, 35.42129], [-80.77589, 35.42095], [-80.77551, 35.42084], [-80.77484, 35.42075], [-80.77335, 35.42061], [-80.77282, 35.42064], [-80.77201, 35.42084], [-80.76792, 35.42236], [-80.76747, 35.4226], [-80.76462, 35.42464], [-80.7644, 35.42474], [-80.764, 35.42478], [-80.7631, 35.42467], [-80.76016, 35.42421], [-80.75971, 35.42409], [-80.75839, 35.42362], [-80.75811, 35.42354], [-80.7578, 35.42352], [-80.75713, 35.42358], [-80.75628, 35.42376], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.7534, 35.42141], [-80.75294, 35.42086], [-80.75256, 35.42057], [-80.75208, 35.42031], [-80.75159, 35.42013], [-80.74601, 35.41862], [-80.74558, 35.41847], [-80.74309, 35.41645], [-80.74092, 35.4145], [-80.73984, 35.41359], [-80.73526, 35.41053], [-80.73379, 35.40906], [-80.73343, 35.40876], [-80.73307, 35.40855], [-80.73263, 35.40842], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73068, 35.40803], [-80.72976, 35.40661], [-80.72956, 35.40612], [-80.72908, 35.40393], [-80.72912, 35.40332], [-80.72923, 35.40296], [-80.72939, 35.40268]]}, "date": "2011-12-10", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75277, 35.42072], [-80.75314, 35.42107], [-80.75352, 35.42159], [-80.75726, 35.42794], [-80.75893, 35.42995], [-80.75928, 35.43016], [-80.75977, 35.43028], [-80.75977, 35.43028], [-80.76035, 35.43043], [-80.7609, 35.43069], [-80.7615, 35.43114], [-80.76222, 35.43187], [-80.76251, 35.43225], [-80.76261, 35.43246], [-80.76266, 35.43278], [-80.76265, 35.43332], [-80.76279, 35.43377], [-80.76308, 35.43413], [-80.76379, 35.43464], [-80.76412, 35.43503], [-80.76434, 35.43565], [-80.76441, 35.4362], [-80.76453, 35.43788], [-80.76452, 35.4382], [-80.76442, 35.43866], [-80.76442, 35.43866], [-80.76392, 35.43969], [-80.76382, 35.4401], [-80.76386, 35.44109], [-80.76401, 35.44217], [-80.76426, 35.44283], [-80.7658, 35.4448], [-80.76597, 35.44519], [-80.76602, 35.44555], [-80.76596, 35.44596], [-80.76568, 35.44642], [-80.76487, 35.44716], [-80.76418, 35.44801], [-80.76401, 35.44829], [-80.76397, 35.44856], [-80.76409, 35.44928], [-80.76409, 35.44955], [-80.76402, 35.45106], [-80.76393, 35.4515], [-80.76371, 35.45188], [-80.76308, 35.45271], [-80.76264, 35.45402], [-80.76246, 35.45445], [-80.76209, 35.4551], [-80.76173, 35.45558], [-80.76115, 35.45618], [-80.75998, 35.45718], [-80.75959, 35.45762], [-80.75939, 35.45793], [-80.75932, 35.45819], [-80.75922, 35.45922], [-80.75855, 35.46121], [-80.75779, 35.46275], [-80.75738, 35.46478], [-80.75633, 35.46824], [-80.75619, 35.46857], [-80.75016, 35.47668], [-80.75016, 35.47668], [-80.75134, 35.47748], [-80.75278, 35.47857], [-80.75522, 35.48055], [-80.75605, 35.48133], [-80.75679, 35.48229], [-80.75731, 35.48316], [-80.76001, 35.48856], [-80.76001, 35.48856], [-80.76471, 35.49024], [-80.76471, 35.49024], [-80.76692, 35.48734], [-80.77014, 35.48202], [-80.77029, 35.48141], [-80.77043, 35.47728], [-80.77051, 35.47695], [-80.77063, 35.47675], [-80.77129, 35.47627], [-80.77338, 35.47499], [-80.77385, 35.47479], [-80.77466, 35.47456], [-80.77496, 35.47451], [-80.77555, 35.47452], [-80.78021, 35.47508], [-80.78021, 35.47508], [-80.78108, 35.47516], [-80.7814, 35.47515], [-80.78169, 35.47508], [-80.78257, 35.47469], [-80.78695, 35.47255], [-80.78738, 35.47238], [-80.78884, 35.47203], [-80.78955, 35.47199], [-80.79018, 35.47206], [-80.79672, 35.47415], [-80.79748, 35.47426], [-80.7998, 35.47442], [-80.8005, 35.47461], [-80.80099, 35.47483], [-80.80147, 35.47516], [-80.80192, 35.47563], [-80.80515, 35.48001], [-80.80552, 35.48044], [-80.80593, 35.48074], [-80.80689, 35.48129], [-80.80988, 35.48296], [-80.81042, 35.48322], [-80.81114, 35.48342], [-80.81157, 35.48346], [-80.81796, 35.48335], [-80.81835, 35.48338], [-80.81883, 35.48349], [-80.81934, 35.48377], [-80.8196, 35.48397], [-80.8196, 35.48397], [-80.81962, 35.48407], [-80.81972, 35.48414], [-80.81984, 35.48416], [-80.81994, 35.4841], [-80.81997, 35.48401], [-80.81992, 35.4839], [-80.82006, 35.48347], [-80.82004, 35.48142], [-80.81995, 35.47924], [-80.81968, 35.47795], [-80.81916, 35.47674], [-80.8167, 35.47246], [-80.81639, 35.4716], [-80.81604, 35.47019], [-80.81576, 35.46964], [-80.81506, 35.46871], [-80.81487, 35.46838], [-80.81437, 35.46702], [-80.81418, 35.46665], [-80.81418, 35.46665], [-80.81522, 35.46547], [-80.81566, 35.4651], [-80.81613, 35.46482], [-80.81719, 35.46437], [-80.81764, 35.46405], [-80.81791, 35.46372], [-80.81807, 35.46337], [-80.81849, 35.46206], [-80.81887, 35.46152], [-80.81938, 35.46116], [-80.82021, 35.46069], [-80.82068, 35.46043], [-80.82105, 35.4603], [-80.82105, 35.4603], [-80.82082, 35.4592], [-80.8208, 35.4589], [-80.82114, 35.45733], [-80.82114, 35.45733], [-80.82191, 35.45736], [-80.82235, 35.45729], [-80.82292, 35.4571], [-80.82346, 35.45677], [-80.8248, 35.45557], [-80.82569, 35.45508], [-80.82569, 35.45508], [-80.82519, 35.45428], [-80.82478, 35.45344], [-80.82468, 35.4531], [-80.82448, 35.45284], [-80.82427, 35.45275], [-80.82243, 35.45225], [-80.82106, 35.45175], [-80.82032, 35.45139], [-80.81947, 35.45075], [-80.81947, 35.45075], [-80.80954, 35.45441], [-80.80954, 35.45441], [-80.80916, 35.4537], [-80.80885, 35.45328], [-80.80874, 35.45302], [-80.80875, 35.45268], [-80.80903, 35.45212], [-80.8091, 35.4517], [-80.80901, 35.45134], [-80.80835, 35.44964], [-80.8083, 35.44934], [-80.80843, 35.44883], [-80.8092, 35.44677], [-80.80935, 35.44614], [-80.80933, 35.44579], [-80.80882, 35.4434], [-80.80893, 35.44234], [-80.80915, 35.44185], [-80.80997, 35.44059], [-80.81098, 35.43885], [-80.81108, 35.43807], [-80.81127, 35.43767], [-80.81266, 35.43567], [-80.81266, 35.43567], [-80.81239, 35.43564], [-80.80952, 35.43566], [-80.80873, 35.43561], [-80.80834, 35.43552], [-80.80809, 35.43541], [-80.80764, 35.43509], [-80.80696, 35.43446], [-80.8058, 35.43394], [-80.80532, 35.43386], [-80.80441, 35.43388], [-80.80358, 35.43395], [-80.80358, 35.43395], [-80.80308, 35.43501], [-80.80302, 35.43527], [-80.80293, 35.43653], [-80.80293, 35.43653], [-80.80274, 35.43638], [-80.80167, 35.43512], [-80.7996, 35.43312], [-80.79428, 35.42758], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79406, 35.42406], [-80.79483, 35.42324], [-80.79732, 35.42077], [-80.79863, 35.41961], [-80.79885, 35.41926], [-80.80057, 35.41562], [-80.80089, 35.41512], [-80.80186, 35.41438], [-80.8022, 35.41425], [-80.80461, 35.41366], [-80.80461, 35.41366], [-80.80471, 35.41173], [-80.80469, 35.41138], [-80.80462, 35.41114], [-80.80437, 35.41073], [-80.80356, 35.40982], [-80.80334, 35.40933], [-80.8031, 35.40852], [-80.80282, 35.40789], [-80.80251, 35.40739], [-80.80203, 35.40691], [-80.80179, 35.40653], [-80.80133, 35.4049], [-80.80126, 35.40431], [-80.80129, 35.40417], [-80.80147, 35.40387], [-80.80186, 35.40341], [-80.80349, 35.40201], [-80.80366, 35.40169], [-80.80368, 35.40065], [-80.8035, 35.3993], [-80.80238, 35.39644], [-80.80226, 35.39591], [-80.80234, 35.39557], [-80.80272, 35.3948], [-80.80313, 35.39375], [-80.80431, 35.39016], [-80.80459, 35.38905], [-80.80465, 35.38868], [-80.80463, 35.38838], [-80.80426, 35.38685], [-80.8042, 35.38641], [-80.80425, 35.3853], [-80.80437, 35.38469], [-80.80457, 35.3844], [-80.80485, 35.38417], [-80.80551, 35.38377], [-80.80646, 35.38332], [-80.80968, 35.38193], [-80.80989, 35.38181], [-80.80998, 35.38165], [-80.80999, 35.38137], [-80.80989, 35.38108], [-80.80877, 35.37857], [-80.80872, 35.37813], [-80.80881, 35.37619], [-80.80859, 35.37423], [-80.80839, 35.37338], [-80.80821, 35.37295], [-80.8077, 35.37243], [-80.80745, 35.37203], [-80.80745, 35.37203], [-80.80574, 35.37294], [-80.80313, 35.37445], [-80.7944, 35.3804], [-80.79396, 35.38059], [-80.78991, 35.38187], [-80.78948, 35.38206], [-80.78844, 35.38275], [-80.78813, 35.383], [-80.78686, 35.38424], [-80.78668, 35.38429], [-80.78668, 35.38429], [-80.78628, 35.38251], [-80.7859, 35.3803], [-80.78501, 35.37401], [-80.78501, 35.37401], [-80.78493, 35.37402], [-80.78471, 35.37393], [-80.78345, 35.37329], [-80.78327, 35.37313], [-80.78319, 35.37294], [-80.78319, 35.37294], [-80.78269, 35.37283], [-80.7817, 35.37251], [-80.78139, 35.37247], [-80.78075, 35.37251], [-80.77909, 35.37297], [-80.77405, 35.37449], [-80.77076, 35.37531], [-80.77037, 35.37537], [-80.77002, 35.37536], [-80.76961, 35.37529], [-80.76517, 35.37399], [-80.76413, 35.37375], [-80.76381, 35.3737], [-80.76228, 35.37364], [-80.76166, 35.37353], [-80.76079, 35.37315], [-80.7582, 35.37169], [-80.75623, 35.371], [-80.75575, 35.37073], [-80.75496, 35.37006], [-80.75376, 35.36863], [-80.75176, 35.36689], [-80.74977, 35.36557], [-80.74935, 35.36524], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74841, 35.36441], [-80.74807, 35.36734], [-80.74768, 35.36903], [-80.74752, 35.36956], [-80.74734, 35.36994], [-80.74705, 35.37032], [-80.74668, 35.37067], [-80.74567, 35.37137], [-80.74519, 35.37161], [-80.7433, 35.3722], [-80.74276, 35.37251], [-80.74218, 35.37306], [-80.74182, 35.37368], [-80.7397, 35.37996], [-80.7396, 35.38007], [-80.7396, 35.38007], [-80.73754, 35.37989], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.72937, 35.38327], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73029, 35.39311]]}, "date": "2011-12-14", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72908, 35.40378], [-80.72912, 35.40332], [-80.72923, 35.40296], [-80.72939, 35.40268], [-80.72999, 35.40205], [-80.73019, 35.40173], [-80.7309, 35.39864], [-80.73051, 35.39643], [-80.73034, 35.39461], [-80.7302, 35.38882], [-80.72996, 35.38803], [-80.72911, 35.38634], [-80.72899, 35.38591], [-80.72904, 35.38526], [-80.7294, 35.38313], [-80.72947, 35.38294], [-80.72993, 35.38223], [-80.73056, 35.3817], [-80.73168, 35.38118], [-80.73298, 35.38066], [-80.73431, 35.38006], [-80.73496, 35.37958], [-80.73557, 35.3789], [-80.73557, 35.3789], [-80.73695, 35.37964], [-80.73754, 35.37989], [-80.73972, 35.3801], [-80.73972, 35.3801], [-80.7397, 35.37996], [-80.74169, 35.37404], [-80.74194, 35.37342], [-80.74209, 35.37318], [-80.74263, 35.37261], [-80.7431, 35.3723], [-80.74372, 35.37205], [-80.74519, 35.37161], [-80.74567, 35.37137], [-80.74668, 35.37067], [-80.74705, 35.37032], [-80.74734, 35.36994], [-80.74752, 35.36956], [-80.74768, 35.36903], [-80.74807, 35.36734], [-80.74841, 35.36441], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74844, 35.36387], [-80.74846, 35.36343], [-80.74872, 35.36212], [-80.74897, 35.36103], [-80.74949, 35.35954], [-80.74949, 35.35954], [-80.75231, 35.35917], [-80.75292, 35.35903], [-80.75334, 35.35879], [-80.75541, 35.35729], [-80.75584, 35.35695], [-80.75633, 35.3564], [-80.7569, 35.35532], [-80.75901, 35.35086], [-80.75909, 35.35051], [-80.75928, 35.3473], [-80.75946, 35.3467], [-80.7596, 35.34645], [-80.75986, 35.34614], [-80.76019, 35.34586], [-80.76407, 35.34305], [-80.76729, 35.34185], [-80.7686, 35.34131], [-80.76947, 35.34083], [-80.77001, 35.34036], [-80.77001, 35.34024], [-80.77042, 35.3397], [-80.77064, 35.33925], [-80.77295, 35.33433], [-80.7731, 35.33387], [-80.77321, 35.33334], [-80.77323, 35.33275], [-80.77317, 35.33216], [-80.77303, 35.33169], [-80.77226, 35.32993], [-80.77219, 35.32946], [-80.77225, 35.32902], [-80.77248, 35.3284], [-80.77294, 35.32741], [-80.77462, 35.32233], [-80.77509, 35.32159], [-80.7757, 35.32099], [-80.7757, 35.32099], [-80.77553, 35.32084], [-80.77541, 35.32064], [-80.77534, 35.32028], [-80.776, 35.31831], [-80.77611, 35.31823], [-80.77639, 35.31746], [-80.77639, 35.31746], [-80.77678, 35.31614], [-80.77676, 35.31371], [-80.7773, 35.31246], [-80.77787, 35.31142], [-80.7781, 35.31085], [-80.77803, 35.31009], [-80.77784, 35.30969], [-80.77711, 35.30866], [-80.77636, 35.30741], [-80.7757, 35.30654], [-80.77481, 35.30577], [-80.77216, 35.30389], [-80.77216, 35.30389], [-80.7725, 35.30354], [-80.77289, 35.30323], [-80.77415, 35.30262], [-80.775, 35.30175], [-80.77529, 35.3012], [-80.77659, 35.29789], [-80.77678, 35.2976], [-80.77735, 35.29716], [-80.78005, 35.29548], [-80.78005, 35.29548], [-80.7775, 35.29406], [-80.77563, 35.2932], [-80.76702, 35.29029], [-80.76588, 35.28998], [-80.76537, 35.2899], [-80.76441, 35.2899], [-80.76072, 35.29016], [-80.76021, 35.29025], [-80.75885, 35.29062], [-80.75827, 35.29071], [-80.75796, 35.29069], [-80.75749, 35.29053], [-80.75749, 35.29053], [-80.75946, 35.28838], [-80.76047, 35.28735], [-80.76128, 35.28662], [-80.76266, 35.28559], [-80.76348, 35.28485], [-80.76369, 35.2846], [-80.76446, 35.28337], [-80.76567, 35.28067], [-80.76567, 35.28067], [-80.7655, 35.28062], [-80.7653, 35.28118], [-80.7649, 35.28198], [-80.76454, 35.28255], [-80.76387, 35.28334], [-80.76387, 35.28334], [-80.76282, 35.28296], [-80.76259, 35.2827], [-80.76259, 35.2827], [-80.76268, 35.28244], [-80.76264, 35.28197], [-80.76237, 35.28041], [-80.76225, 35.28004], [-80.76161, 35.2788], [-80.76142, 35.2786], [-80.76075, 35.27822], [-80.76013, 35.27741], [-80.76013, 35.27741], [-80.75899, 35.27799], [-80.75857, 35.2781], [-80.75824, 35.27809], [-80.75791, 35.27798], [-80.75745, 35.27762], [-80.75745, 35.27762], [-80.7554, 35.27867], [-80.75513, 35.27886], [-80.75488, 35.27914], [-80.75488, 35.27914], [-80.75343, 35.27835]]}, "date": "2011-12-15", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.68783, 35.41225], [-80.68797, 35.4127], [-80.68809, 35.41289], [-80.6884, 35.41309], [-80.6888, 35.41321], [-80.69008, 35.41336], [-80.69072, 35.41335], [-80.69231, 35.41318], [-80.69373, 35.41292], [-80.69461, 35.41272], [-80.69491, 35.41258], [-80.69518, 35.41239], [-80.69543, 35.41216], [-80.69574, 35.41172], [-80.69612, 35.41075], [-80.69639, 35.41021], [-80.69846, 35.40688], [-80.69999, 35.40409], [-80.70084, 35.40304], [-80.70107, 35.40269], [-80.70107, 35.40269], [-80.70393, 35.40368], [-80.70565, 35.40435], [-80.71314, 35.40736], [-80.71716, 35.40911], [-80.71809, 35.4094], [-80.72187, 35.4098], [-80.72254, 35.40981], [-80.72326, 35.40966], [-80.72802, 35.40826], [-80.72892, 35.40821], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73068, 35.40803], [-80.73004, 35.40709], [-80.72961, 35.40628], [-80.72912, 35.40425], [-80.72908, 35.40378]]}, "date": "2011-12-17", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75256, 35.42057], [-80.75294, 35.42086], [-80.7534, 35.42141], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.75628, 35.42376], [-80.75713, 35.42358], [-80.7578, 35.42352], [-80.75811, 35.42354], [-80.75839, 35.42362], [-80.75971, 35.42409], [-80.76016, 35.42421], [-80.7631, 35.42467], [-80.764, 35.42478], [-80.7644, 35.42474], [-80.76462, 35.42464], [-80.76747, 35.4226], [-80.76792, 35.42236], [-80.77201, 35.42084], [-80.77282, 35.42064], [-80.77335, 35.42061], [-80.77484, 35.42075], [-80.77551, 35.42084], [-80.77589, 35.42095], [-80.77666, 35.42129], [-80.77713, 35.42156], [-80.77754, 35.4219], [-80.77798, 35.4225], [-80.77821, 35.42269], [-80.77896, 35.42292], [-80.78116, 35.42347], [-80.78176, 35.42352], [-80.78718, 35.4232], [-80.7875, 35.42319], [-80.7879, 35.42324], [-80.78822, 35.42335], [-80.79105, 35.42488], [-80.79201, 35.42529], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79406, 35.42406], [-80.79483, 35.42324], [-80.79732, 35.42077], [-80.79863, 35.41961], [-80.79885, 35.41926], [-80.80057, 35.41562], [-80.80089, 35.41512], [-80.80186, 35.41438], [-80.8022, 35.41425], [-80.80461, 35.41366], [-80.80461, 35.41366], [-80.80471, 35.41173], [-80.80469, 35.41138], [-80.80462, 35.41114], [-80.80437, 35.41073], [-80.80356, 35.40982], [-80.80334, 35.40933], [-80.8031, 35.40852], [-80.80282, 35.40789], [-80.80251, 35.40739], [-80.80203, 35.40691], [-80.80179, 35.40653], [-80.80133, 35.4049], [-80.80126, 35.40431], [-80.80129, 35.40417], [-80.80147, 35.40387], [-80.80186, 35.40341], [-80.80349, 35.40201], [-80.80366, 35.40169], [-80.80368, 35.40065], [-80.8035, 35.3993], [-80.80238, 35.39644], [-80.80226, 35.39591], [-80.80234, 35.39557], [-80.80272, 35.3948], [-80.80313, 35.39375], [-80.80431, 35.39016], [-80.80459, 35.38905], [-80.80465, 35.38868], [-80.80463, 35.38838], [-80.80426, 35.38685], [-80.8042, 35.38641], [-80.80425, 35.3853], [-80.80437, 35.38469], [-80.80457, 35.3844], [-80.80485, 35.38417], [-80.80551, 35.38377], [-80.80646, 35.38332], [-80.80968, 35.38193], [-80.80989, 35.38181], [-80.80998, 35.38165], [-80.80999, 35.38137], [-80.80989, 35.38108], [-80.80877, 35.37857], [-80.80872, 35.37813], [-80.80881, 35.37619], [-80.80859, 35.37423], [-80.80839, 35.37338], [-80.80821, 35.37295], [-80.8077, 35.37243], [-80.80745, 35.37203], [-80.80745, 35.37203], [-80.80574, 35.37294], [-80.80313, 35.37445], [-80.7944, 35.3804], [-80.79396, 35.38059], [-80.78991, 35.38187], [-80.78948, 35.38206], [-80.78844, 35.38275], [-80.78813, 35.383], [-80.78686, 35.38424], [-80.78668, 35.38429], [-80.78668, 35.38429], [-80.78628, 35.38251], [-80.7859, 35.3803], [-80.78501, 35.37401], [-80.78501, 35.37401], [-80.78493, 35.37402], [-80.78471, 35.37393], [-80.78345, 35.37329], [-80.78327, 35.37313], [-80.78319, 35.37294], [-80.78319, 35.37294], [-80.78269, 35.37283], [-80.7817, 35.37251], [-80.78139, 35.37247], [-80.78075, 35.37251], [-80.77909, 35.37297], [-80.77405, 35.37449], [-80.77076, 35.37531], [-80.77037, 35.37537], [-80.77002, 35.37536], [-80.76961, 35.37529], [-80.76517, 35.37399], [-80.76413, 35.37375], [-80.76381, 35.3737], [-80.76228, 35.37364], [-80.76166, 35.37353], [-80.76079, 35.37315], [-80.7582, 35.37169], [-80.75623, 35.371], [-80.75575, 35.37073], [-80.75496, 35.37006], [-80.75376, 35.36863], [-80.75176, 35.36689], [-80.74977, 35.36557], [-80.74935, 35.36524], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74841, 35.36441], [-80.74807, 35.36734], [-80.74768, 35.36903], [-80.74752, 35.36956], [-80.74734, 35.36994], [-80.74705, 35.37032], [-80.74668, 35.37067], [-80.74567, 35.37137], [-80.74519, 35.37161], [-80.7433, 35.3722], [-80.74276, 35.37251], [-80.74218, 35.37306], [-80.74182, 35.37368], [-80.7397, 35.37996], [-80.7396, 35.38007], [-80.7396, 35.38007], [-80.73754, 35.37989], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.72937, 35.38327], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73029, 35.39311]]}, "date": "2011-12-18", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75256, 35.42057], [-80.75294, 35.42086], [-80.7534, 35.42141], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.75628, 35.42376], [-80.75713, 35.42358], [-80.7578, 35.42352], [-80.75811, 35.42354], [-80.75839, 35.42362], [-80.75971, 35.42409], [-80.76016, 35.42421], [-80.7631, 35.42467], [-80.764, 35.42478], [-80.7644, 35.42474], [-80.76462, 35.42464], [-80.76747, 35.4226], [-80.76792, 35.42236], [-80.77201, 35.42084], [-80.77282, 35.42064], [-80.77335, 35.42061], [-80.77484, 35.42075], [-80.77551, 35.42084], [-80.77589, 35.42095], [-80.77666, 35.42129], [-80.77713, 35.42156], [-80.77754, 35.4219], [-80.77798, 35.4225], [-80.77821, 35.42269], [-80.77896, 35.42292], [-80.78116, 35.42347], [-80.78176, 35.42352], [-80.78718, 35.4232], [-80.7875, 35.42319], [-80.7879, 35.42324], [-80.78822, 35.42335], [-80.79105, 35.42488], [-80.79201, 35.42529], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79406, 35.42406], [-80.79483, 35.42324], [-80.79732, 35.42077], [-80.79863, 35.41961], [-80.79885, 35.41926], [-80.80057, 35.41562], [-80.80089, 35.41512], [-80.80186, 35.41438], [-80.8022, 35.41425], [-80.80461, 35.41366], [-80.80461, 35.41366], [-80.80471, 35.41173], [-80.80469, 35.41138], [-80.80462, 35.41114], [-80.80437, 35.41073], [-80.80356, 35.40982], [-80.80334, 35.40933], [-80.8031, 35.40852], [-80.80282, 35.40789], [-80.80251, 35.40739], [-80.80203, 35.40691], [-80.80179, 35.40653], [-80.80133, 35.4049], [-80.80126, 35.40431], [-80.80129, 35.40417], [-80.80147, 35.40387], [-80.80186, 35.40341], [-80.80349, 35.40201], [-80.80366, 35.40169], [-80.80368, 35.40065], [-80.8035, 35.3993], [-80.80238, 35.39644], [-80.80226, 35.39591], [-80.80234, 35.39557], [-80.80272, 35.3948], [-80.80313, 35.39375], [-80.80431, 35.39016], [-80.80459, 35.38905], [-80.80465, 35.38868], [-80.80463, 35.38838], [-80.80426, 35.38685], [-80.8042, 35.38641], [-80.80425, 35.3853], [-80.80437, 35.38469], [-80.80457, 35.3844], [-80.80485, 35.38417], [-80.80551, 35.38377], [-80.80646, 35.38332], [-80.80968, 35.38193], [-80.80989, 35.38181], [-80.80998, 35.38165], [-80.80999, 35.38137], [-80.80989, 35.38108], [-80.80877, 35.37857], [-80.80872, 35.37813], [-80.80881, 35.37619], [-80.80859, 35.37423], [-80.80839, 35.37338], [-80.80821, 35.37295], [-80.8077, 35.37243], [-80.80745, 35.37203], [-80.80745, 35.37203], [-80.80574, 35.37294], [-80.80313, 35.37445], [-80.7944, 35.3804], [-80.79396, 35.38059], [-80.78991, 35.38187], [-80.78948, 35.38206], [-80.78844, 35.38275], [-80.78813, 35.383], [-80.78686, 35.38424], [-80.78668, 35.38429], [-80.78668, 35.38429], [-80.78628, 35.38251], [-80.7859, 35.3803], [-80.78501, 35.37401], [-80.78501, 35.37401], [-80.78493, 35.37402], [-80.78471, 35.37393], [-80.78345, 35.37329], [-80.78327, 35.37313], [-80.78319, 35.37294], [-80.78319, 35.37294], [-80.78269, 35.37283], [-80.7817, 35.37251], [-80.78139, 35.37247], [-80.78075, 35.37251], [-80.77909, 35.37297], [-80.77389, 35.37453], [-80.77389, 35.37453], [-80.77456, 35.3761], [-80.77563, 35.37794], [-80.77577, 35.37835], [-80.77567, 35.37886], [-80.77546, 35.37917], [-80.77464, 35.38006], [-80.77404, 35.38036], [-80.77344, 35.38045], [-80.77097, 35.38027], [-80.77062, 35.38029], [-80.77026, 35.38037], [-80.76917, 35.38089], [-80.76852, 35.38104], [-80.76801, 35.38102], [-80.76618, 35.3807], [-80.76559, 35.38058], [-80.76539, 35.3805], [-80.7643, 35.3799], [-80.76263, 35.37887], [-80.76192, 35.37851], [-80.75888, 35.37738], [-80.75844, 35.37727], [-80.75781, 35.37724], [-80.75731, 35.37728], [-80.75667, 35.37747], [-80.75602, 35.37785], [-80.75562, 35.37824], [-80.7553, 35.3788], [-80.75442, 35.38099], [-80.75442, 35.38099], [-80.75238, 35.38084], [-80.75169, 35.38072], [-80.75118, 35.38047], [-80.75038, 35.37981], [-80.7494, 35.37945], [-80.74884, 35.37936], [-80.7482, 35.37943], [-80.74531, 35.38097], [-80.74512, 35.38101], [-80.74512, 35.38101], [-80.74497, 35.38099], [-80.74487, 35.38112], [-80.74446, 35.38125], [-80.74417, 35.38128], [-80.74362, 35.38123], [-80.74006, 35.38016], [-80.7396, 35.38007], [-80.73766, 35.37992], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.72937, 35.38327], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73029, 35.39311]]}, "date": "2011-12-19", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.76399, 35.44203], [-80.76426, 35.44283], [-80.7658, 35.4448], [-80.76597, 35.44519], [-80.76602, 35.44555], [-80.76596, 35.44596], [-80.76568, 35.44642], [-80.76487, 35.44716], [-80.76418, 35.44801], [-80.76401, 35.44829], [-80.76397, 35.44856], [-80.76409, 35.44928], [-80.76409, 35.44955], [-80.76402, 35.45106], [-80.76393, 35.4515], [-80.76371, 35.45188], [-80.76308, 35.45271], [-80.76264, 35.45402], [-80.76246, 35.45445], [-80.76209, 35.4551], [-80.76173, 35.45558], [-80.76115, 35.45618], [-80.75998, 35.45718], [-80.75959, 35.45762], [-80.75939, 35.45793], [-80.75932, 35.45819], [-80.75922, 35.45922], [-80.75855, 35.46121], [-80.75779, 35.46275], [-80.75738, 35.46478], [-80.75633, 35.46824], [-80.75619, 35.46857], [-80.75016, 35.47668], [-80.75016, 35.47668], [-80.75134, 35.47748], [-80.75278, 35.47857], [-80.75522, 35.48055], [-80.75605, 35.48133], [-80.75679, 35.48229], [-80.75731, 35.48316], [-80.76001, 35.48856], [-80.76001, 35.48856], [-80.76471, 35.49024], [-80.76471, 35.49024], [-80.76692, 35.48734], [-80.77014, 35.48202], [-80.77029, 35.48141], [-80.77043, 35.47728], [-80.77051, 35.47695], [-80.77063, 35.47675], [-80.77129, 35.47627], [-80.77338, 35.47499], [-80.77385, 35.47479], [-80.77466, 35.47456], [-80.77496, 35.47451], [-80.77555, 35.47452], [-80.78021, 35.47508], [-80.78021, 35.47508], [-80.78108, 35.47516], [-80.7814, 35.47515], [-80.78169, 35.47508], [-80.78257, 35.47469], [-80.78695, 35.47255], [-80.78738, 35.47238], [-80.78884, 35.47203], [-80.78955, 35.47199], [-80.79018, 35.47206], [-80.79672, 35.47415], [-80.79748, 35.47426], [-80.7998, 35.47442], [-80.8005, 35.47461], [-80.80099, 35.47483], [-80.80147, 35.47516], [-80.80192, 35.47563], [-80.80515, 35.48001], [-80.80552, 35.48044], [-80.80593, 35.48074], [-80.80689, 35.48129], [-80.80988, 35.48296], [-80.81042, 35.48322], [-80.81114, 35.48342], [-80.81157, 35.48346], [-80.81796, 35.48335], [-80.81835, 35.48338], [-80.81883, 35.48349], [-80.81934, 35.48377], [-80.8196, 35.48397], [-80.8196, 35.48397], [-80.81962, 35.48407], [-80.81972, 35.48414], [-80.81984, 35.48416], [-80.81994, 35.4841], [-80.81997, 35.48401], [-80.81992, 35.4839], [-80.82006, 35.48347], [-80.82004, 35.48142], [-80.81995, 35.47924], [-80.81968, 35.47795], [-80.81916, 35.47674], [-80.8167, 35.47246], [-80.81639, 35.4716], [-80.81604, 35.47019], [-80.81576, 35.46964], [-80.81506, 35.46871], [-80.81487, 35.46838], [-80.81437, 35.46702], [-80.81418, 35.46665], [-80.81418, 35.46665], [-80.81522, 35.46547], [-80.81566, 35.4651], [-80.81613, 35.46482], [-80.81719, 35.46437], [-80.81764, 35.46405], [-80.81791, 35.46372], [-80.81807, 35.46337], [-80.81849, 35.46206], [-80.81887, 35.46152], [-80.81938, 35.46116], [-80.82021, 35.46069], [-80.82068, 35.46043], [-80.82105, 35.4603], [-80.82105, 35.4603], [-80.82082, 35.4592], [-80.8208, 35.4589], [-80.82114, 35.45733], [-80.82114, 35.45733], [-80.82191, 35.45736], [-80.82235, 35.45729], [-80.82292, 35.4571], [-80.82346, 35.45677], [-80.8248, 35.45557], [-80.82569, 35.45508], [-80.82569, 35.45508], [-80.82519, 35.45428], [-80.82478, 35.45344], [-80.82468, 35.4531], [-80.82448, 35.45284], [-80.82427, 35.45275], [-80.82243, 35.45225], [-80.82106, 35.45175], [-80.82032, 35.45139], [-80.81947, 35.45075], [-80.81947, 35.45075], [-80.80954, 35.45441], [-80.80954, 35.45441], [-80.80916, 35.4537], [-80.80885, 35.45328], [-80.80874, 35.45302], [-80.80875, 35.45268], [-80.80903, 35.45212], [-80.8091, 35.4517], [-80.80901, 35.45134], [-80.80835, 35.44964], [-80.8083, 35.44934], [-80.80843, 35.44883], [-80.8092, 35.44677], [-80.80935, 35.44614], [-80.80933, 35.44579], [-80.80882, 35.4434], [-80.80893, 35.44234], [-80.80915, 35.44185], [-80.80997, 35.44059], [-80.81098, 35.43885], [-80.81108, 35.43807], [-80.81127, 35.43767], [-80.81266, 35.43567], [-80.81266, 35.43567], [-80.81239, 35.43564], [-80.80952, 35.43566], [-80.80873, 35.43561], [-80.80834, 35.43552], [-80.80809, 35.43541], [-80.80764, 35.43509], [-80.80696, 35.43446], [-80.8058, 35.43394], [-80.80532, 35.43386], [-80.80441, 35.43388], [-80.80358, 35.43395], [-80.80358, 35.43395], [-80.80308, 35.43501], [-80.80302, 35.43527], [-80.80293, 35.43653], [-80.80293, 35.43653], [-80.80274, 35.43638], [-80.80167, 35.43512], [-80.7996, 35.43312], [-80.79428, 35.42758], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79201, 35.42529], [-80.79105, 35.42488], [-80.78822, 35.42335], [-80.7879, 35.42324], [-80.7875, 35.42319], [-80.78718, 35.4232], [-80.78176, 35.42352], [-80.78116, 35.42347], [-80.77896, 35.42292], [-80.77821, 35.42269], [-80.77798, 35.4225], [-80.77754, 35.4219], [-80.77713, 35.42156], [-80.77666, 35.42129], [-80.77589, 35.42095], [-80.77551, 35.42084], [-80.77484, 35.42075], [-80.77335, 35.42061], [-80.77282, 35.42064], [-80.77201, 35.42084], [-80.76792, 35.42236], [-80.76747, 35.4226], [-80.76462, 35.42464], [-80.7644, 35.42474], [-80.764, 35.42478], [-80.7631, 35.42467], [-80.76016, 35.42421], [-80.75971, 35.42409], [-80.75839, 35.42362], [-80.75811, 35.42354], [-80.7578, 35.42352], [-80.75713, 35.42358], [-80.75628, 35.42376], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.7571, 35.42771], [-80.75756, 35.42833], [-80.75893, 35.42995], [-80.75928, 35.43016], [-80.75977, 35.43028], [-80.75977, 35.43028], [-80.76035, 35.43043], [-80.7609, 35.43069], [-80.7615, 35.43114], [-80.76222, 35.43187], [-80.76251, 35.43225], [-80.76261, 35.43246], [-80.76266, 35.43278], [-80.76265, 35.43332], [-80.76279, 35.43377], [-80.76308, 35.43413], [-80.76379, 35.43464], [-80.76412, 35.43503], [-80.76434, 35.43565], [-80.76441, 35.4362], [-80.76453, 35.43788], [-80.76452, 35.4382], [-80.76442, 35.43866], [-80.76442, 35.43866], [-80.76392, 35.43969], [-80.76382, 35.4401], [-80.76381, 35.44052], [-80.76394, 35.44169]]}, "date": "2012-01-02", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75256, 35.42057], [-80.75294, 35.42086], [-80.7534, 35.42141], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.75628, 35.42376], [-80.75713, 35.42358], [-80.7578, 35.42352], [-80.75811, 35.42354], [-80.75839, 35.42362], [-80.75971, 35.42409], [-80.76016, 35.42421], [-80.7631, 35.42467], [-80.764, 35.42478], [-80.7644, 35.42474], [-80.76462, 35.42464], [-80.76747, 35.4226], [-80.76792, 35.42236], [-80.77201, 35.42084], [-80.77282, 35.42064], [-80.77335, 35.42061], [-80.77484, 35.42075], [-80.77551, 35.42084], [-80.77589, 35.42095], [-80.77666, 35.42129], [-80.77713, 35.42156], [-80.77754, 35.4219], [-80.77798, 35.4225], [-80.77821, 35.42269], [-80.77896, 35.42292], [-80.78116, 35.42347], [-80.78176, 35.42352], [-80.78718, 35.4232], [-80.7875, 35.42319], [-80.7879, 35.42324], [-80.78822, 35.42335], [-80.79105, 35.42488], [-80.79201, 35.42529], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79406, 35.42406], [-80.79483, 35.42324], [-80.79732, 35.42077], [-80.79863, 35.41961], [-80.79885, 35.41926], [-80.80057, 35.41562], [-80.80089, 35.41512], [-80.80186, 35.41438], [-80.8022, 35.41425], [-80.80461, 35.41366], [-80.80461, 35.41366], [-80.80471, 35.41173], [-80.80469, 35.41138], [-80.80462, 35.41114], [-80.80437, 35.41073], [-80.80356, 35.40982], [-80.80334, 35.40933], [-80.8031, 35.40852], [-80.80282, 35.40789], [-80.80251, 35.40739], [-80.80203, 35.40691], [-80.80179, 35.40653], [-80.80133, 35.4049], [-80.80126, 35.40431], [-80.80129, 35.40417], [-80.80147, 35.40387], [-80.80186, 35.40341], [-80.80349, 35.40201], [-80.80366, 35.40169], [-80.80368, 35.40065], [-80.8035, 35.3993], [-80.80238, 35.39644], [-80.80226, 35.39591], [-80.80234, 35.39557], [-80.80272, 35.3948], [-80.80313, 35.39375], [-80.80431, 35.39016], [-80.80459, 35.38905], [-80.80465, 35.38868], [-80.80463, 35.38838], [-80.80426, 35.38685], [-80.8042, 35.38641], [-80.80425, 35.3853], [-80.80437, 35.38469], [-80.80457, 35.3844], [-80.80485, 35.38417], [-80.80551, 35.38377], [-80.80646, 35.38332], [-80.80968, 35.38193], [-80.80989, 35.38181], [-80.80998, 35.38165], [-80.80999, 35.38137], [-80.80989, 35.38108], [-80.80877, 35.37857], [-80.80872, 35.37813], [-80.80881, 35.37619], [-80.80859, 35.37423], [-80.80839, 35.37338], [-80.80821, 35.37295], [-80.8077, 35.37243], [-80.80745, 35.37203], [-80.80745, 35.37203], [-80.80574, 35.37294], [-80.80313, 35.37445], [-80.7944, 35.3804], [-80.79396, 35.38059], [-80.78977, 35.38192], [-80.78905, 35.38232], [-80.78813, 35.383], [-80.78686, 35.38424], [-80.78668, 35.38429], [-80.78586, 35.38513], [-80.78416, 35.3867], [-80.77943, 35.39042], [-80.7786, 35.39092], [-80.77556, 35.39213], [-80.77556, 35.39213], [-80.77503, 35.39166], [-80.77113, 35.38892], [-80.76918, 35.38759], [-80.76869, 35.38736], [-80.7676, 35.38716], [-80.76564, 35.38689], [-80.76521, 35.38686], [-80.76456, 35.3869], [-80.76378, 35.38712], [-80.76102, 35.38827], [-80.76018, 35.38853], [-80.75968, 35.38859], [-80.75884, 35.38856], [-80.75825, 35.38843], [-80.75758, 35.38812], [-80.75704, 35.38776], [-80.75671, 35.38742], [-80.7565, 35.3871], [-80.75585, 35.38557], [-80.75556, 35.38503], [-80.75508, 35.38432], [-80.75433, 35.38348], [-80.75408, 35.38313], [-80.75399, 35.38279], [-80.75402, 35.38243], [-80.75457, 35.38102], [-80.75457, 35.38102], [-80.75208, 35.38081], [-80.75169, 35.38072], [-80.75118, 35.38047], [-80.75038, 35.37981], [-80.7494, 35.37945], [-80.74884, 35.37936], [-80.7482, 35.37943], [-80.74531, 35.38097], [-80.74512, 35.38101], [-80.74512, 35.38101], [-80.74497, 35.38099], [-80.74487, 35.38112], [-80.74446, 35.38125], [-80.74417, 35.38128], [-80.74362, 35.38123], [-80.74006, 35.38016], [-80.7396, 35.38007], [-80.73766, 35.37992], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.72937, 35.38327], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73029, 35.39311]]}, "date": "2012-01-07", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72908, 35.40378], [-80.72912, 35.40425], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74541, 35.41836], [-80.74572, 35.41852], [-80.74572, 35.41852], [-80.74502, 35.41897], [-80.74457, 35.41942], [-80.74438, 35.41972], [-80.74357, 35.42151], [-80.74245, 35.42302], [-80.74173, 35.42365], [-80.73997, 35.42449], [-80.73972, 35.4247], [-80.73932, 35.42516], [-80.73816, 35.42668], [-80.73642, 35.42943], [-80.73609, 35.42975], [-80.73579, 35.4299], [-80.7314, 35.43146], [-80.72992, 35.43235], [-80.7293, 35.43265], [-80.72694, 35.43335], [-80.72485, 35.43371], [-80.72485, 35.43371], [-80.72709, 35.43871], [-80.72779, 35.44124], [-80.72829, 35.44222], [-80.72956, 35.44443], [-80.72981, 35.44498], [-80.73005, 35.44569], [-80.73116, 35.45106], [-80.73178, 35.45356], [-80.73204, 35.455], [-80.73232, 35.4599], [-80.73239, 35.46039], [-80.73239, 35.46039], [-80.73192, 35.46179], [-80.7301, 35.46601], [-80.73, 35.46628], [-80.72993, 35.46673], [-80.73003, 35.4684], [-80.73073, 35.47428], [-80.73074, 35.47717], [-80.73053, 35.47774], [-80.73002, 35.47847], [-80.72737, 35.48121], [-80.72737, 35.48121], [-80.72712, 35.48104], [-80.72674, 35.48088], [-80.72635, 35.48077], [-80.72578, 35.4807], [-80.72384, 35.48068], [-80.7201, 35.48076], [-80.71857, 35.48073], [-80.71793, 35.48077], [-80.71668, 35.48107], [-80.71242, 35.48242], [-80.70905, 35.48315], [-80.70905, 35.48315], [-80.70683, 35.47997], [-80.70574, 35.47831], [-80.70514, 35.47725], [-80.70448, 35.47567], [-80.70404, 35.47521], [-80.70366, 35.475], [-80.70366, 35.475], [-80.7035, 35.47496], [-80.70286, 35.47496], [-80.70236, 35.47499], [-80.70185, 35.47508], [-80.70158, 35.47516], [-80.70129, 35.47531], [-80.70106, 35.4756], [-80.70101, 35.47576], [-80.70101, 35.47576], [-80.70046, 35.47552], [-80.70019, 35.47535], [-80.69921, 35.47451], [-80.69874, 35.47418], [-80.69737, 35.47346], [-80.6965, 35.47309], [-80.69603, 35.47301], [-80.69548, 35.47301], [-80.69373, 35.47333], [-80.69296, 35.47338], [-80.69193, 35.47329], [-80.6916, 35.47321], [-80.69127, 35.47308], [-80.69058, 35.47271], [-80.69058, 35.47271], [-80.69004, 35.47223], [-80.68982, 35.47171], [-80.68898, 35.46746], [-80.68882, 35.46694], [-80.68789, 35.46527], [-80.68688, 35.46385], [-80.68688, 35.46385], [-80.68701, 35.46377], [-80.68717, 35.46356], [-80.68777, 35.46248], [-80.68787, 35.4622], [-80.68787, 35.46183], [-80.68715, 35.4592], [-80.68647, 35.45513], [-80.68638, 35.45485], [-80.68594, 35.454], [-80.68413, 35.45078], [-80.68339, 35.44929], [-80.68266, 35.44541], [-80.68273, 35.44445], [-80.68287, 35.44395], [-80.6836, 35.44175], [-80.68395, 35.441], [-80.68395, 35.441], [-80.68299, 35.44035], [-80.68217, 35.43997], [-80.68145, 35.43974], [-80.68004, 35.4394], [-80.68004, 35.4394], [-80.68025, 35.43848], [-80.68028, 35.43818], [-80.68024, 35.43811], [-80.68047, 35.43695], [-80.68052, 35.43498], [-80.68048, 35.43428], [-80.68034, 35.43314], [-80.67865, 35.42572], [-80.67857, 35.4255], [-80.67736, 35.42331], [-80.67736, 35.42318], [-80.67555, 35.42096], [-80.67481, 35.42028], [-80.6742, 35.41983], [-80.67388, 35.41964], [-80.67375, 35.41959], [-80.67364, 35.41961], [-80.67303, 35.41927], [-80.67303, 35.41927], [-80.67148, 35.41836], [-80.67027, 35.41757], [-80.66937, 35.41675], [-80.6681, 35.41544], [-80.66777, 35.41504], [-80.66725, 35.41419], [-80.66726, 35.41411], [-80.66689, 35.41345], [-80.66624, 35.41238], [-80.66416, 35.40868], [-80.66198, 35.40464], [-80.66112, 35.40325], [-80.66048, 35.40198], [-80.65988, 35.40052], [-80.65931, 35.39875], [-80.65918, 35.39807], [-80.65918, 35.39807], [-80.66011, 35.3979], [-80.66183, 35.39739], [-80.66299, 35.39702], [-80.66346, 35.3968], [-80.66616, 35.39495], [-80.66651, 35.39475], [-80.66732, 35.39439], [-80.66854, 35.39393], [-80.66893, 35.39372], [-80.67052, 35.39243], [-80.67219, 35.39122], [-80.6738, 35.3903], [-80.67423, 35.39021], [-80.67461, 35.39021], [-80.67634, 35.39059], [-80.67986, 35.39151], [-80.68035, 35.39153], [-80.68057, 35.39145], [-80.68116, 35.39096], [-80.68414, 35.38811], [-80.68504, 35.38731], [-80.68561, 35.38696], [-80.6862, 35.38672], [-80.68762, 35.38644], [-80.68818, 35.38625], [-80.68867, 35.386], [-80.68913, 35.38564], [-80.68945, 35.38529], [-80.68984, 35.38474], [-80.69156, 35.38192], [-80.69232, 35.38117], [-80.6929, 35.38082], [-80.69384, 35.3805], [-80.69587, 35.3802], [-80.69673, 35.38001], [-80.69713, 35.37988], [-80.69766, 35.37962], [-80.69815, 35.37926], [-80.69815, 35.37926], [-80.69841, 35.37907], [-80.69874, 35.37868], [-80.69991, 35.37686], [-80.70007, 35.37663], [-80.70015, 35.37659], [-80.7011, 35.37515], [-80.70169, 35.37454], [-80.70232, 35.37413], [-80.70299, 35.37385], [-80.70433, 35.37354], [-80.70527, 35.37327], [-80.70591, 35.37293], [-80.70644, 35.37252], [-80.70862, 35.37002], [-80.70878, 35.36978], [-80.70893, 35.3695], [-80.7096, 35.36728], [-80.70989, 35.36686], [-80.7102, 35.36653], [-80.7102, 35.36653], [-80.71193, 35.36759], [-80.71506, 35.36965], [-80.71506, 35.36965], [-80.71585, 35.37008], [-80.71704, 35.37092], [-80.71823, 35.37148], [-80.71987, 35.37192], [-80.72216, 35.37266], [-80.72473, 35.37379], [-80.72529, 35.374], [-80.72614, 35.37424], [-80.72678, 35.37436], [-80.72944, 35.37457], [-80.73088, 35.37497], [-80.73088, 35.37497], [-80.73153, 35.37524], [-80.73209, 35.37567], [-80.73233, 35.37597], [-80.73308, 35.37729], [-80.73355, 35.37774], [-80.73557, 35.3789], [-80.73557, 35.3789], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.7294, 35.38313], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73034, 35.39461], [-80.73051, 35.39643], [-80.7309, 35.39864], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.72913, 35.4033]]}, "date": "2012-01-08", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.72892, 35.40821], [-80.72802, 35.40826], [-80.7229, 35.40976], [-80.7223, 35.40982], [-80.72187, 35.4098], [-80.71809, 35.4094], [-80.71772, 35.4093], [-80.71716, 35.40911], [-80.71334, 35.40745], [-80.71334, 35.40745], [-80.7117, 35.41006], [-80.71147, 35.41031], [-80.71023, 35.41139], [-80.70992, 35.41192], [-80.70977, 35.41247], [-80.70914, 35.41998], [-80.70913, 35.42079], [-80.70926, 35.42133], [-80.70952, 35.42201], [-80.71103, 35.42562], [-80.71151, 35.42623], [-80.71209, 35.42663], [-80.71266, 35.42686], [-80.71387, 35.4272], [-80.71456, 35.42759], [-80.71627, 35.42868], [-80.71766, 35.42941], [-80.718, 35.42952], [-80.71963, 35.42982], [-80.72218, 35.43024], [-80.72299, 35.43049], [-80.72349, 35.4309], [-80.7238, 35.43138], [-80.72485, 35.43371], [-80.72485, 35.43371], [-80.72694, 35.43335], [-80.7293, 35.43265], [-80.72992, 35.43235], [-80.7314, 35.43146], [-80.73579, 35.4299], [-80.73609, 35.42975], [-80.73642, 35.42943], [-80.73816, 35.42668], [-80.73932, 35.42516], [-80.73972, 35.4247], [-80.73997, 35.42449], [-80.74173, 35.42365], [-80.74245, 35.42302], [-80.74357, 35.42151], [-80.74438, 35.41972], [-80.74457, 35.41942], [-80.74502, 35.41897], [-80.74572, 35.41852], [-80.74572, 35.41852], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75256, 35.42057], [-80.75294, 35.42086], [-80.7534, 35.42141], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.75628, 35.42376], [-80.75713, 35.42358], [-80.7578, 35.42352], [-80.75811, 35.42354], [-80.75839, 35.42362], [-80.75971, 35.42409], [-80.76016, 35.42421], [-80.7631, 35.42467], [-80.764, 35.42478], [-80.7644, 35.42474], [-80.76462, 35.42464], [-80.76747, 35.4226], [-80.76792, 35.42236], [-80.77201, 35.42084], [-80.77282, 35.42064], [-80.77335, 35.42061], [-80.77484, 35.42075], [-80.77551, 35.42084], [-80.77589, 35.42095], [-80.77666, 35.42129], [-80.77713, 35.42156], [-80.77754, 35.4219], [-80.77798, 35.4225], [-80.77821, 35.42269], [-80.77896, 35.42292], [-80.78116, 35.42347], [-80.78176, 35.42352], [-80.78718, 35.4232], [-80.7875, 35.42319], [-80.7879, 35.42324], [-80.78822, 35.42335], [-80.79105, 35.42488], [-80.79201, 35.42529], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79406, 35.42406], [-80.79483, 35.42324], [-80.79732, 35.42077], [-80.79863, 35.41961], [-80.79885, 35.41926], [-80.80057, 35.41562], [-80.80089, 35.41512], [-80.80186, 35.41438], [-80.8022, 35.41425], [-80.80461, 35.41366], [-80.80461, 35.41366], [-80.80471, 35.41173], [-80.80469, 35.41138], [-80.80462, 35.41114], [-80.80437, 35.41073], [-80.80356, 35.40982], [-80.80334, 35.40933], [-80.8031, 35.40852], [-80.80282, 35.40789], [-80.80251, 35.40739], [-80.80203, 35.40691], [-80.80179, 35.40653], [-80.80133, 35.4049], [-80.80126, 35.40431], [-80.80129, 35.40417], [-80.80147, 35.40387], [-80.80186, 35.40341], [-80.80349, 35.40201], [-80.80366, 35.40169], [-80.80368, 35.40065], [-80.8035, 35.3993], [-80.80238, 35.39644], [-80.80226, 35.39591], [-80.80234, 35.39557], [-80.80272, 35.3948], [-80.80313, 35.39375], [-80.80431, 35.39016], [-80.80459, 35.38905], [-80.80465, 35.38868], [-80.80463, 35.38838], [-80.80426, 35.38685], [-80.8042, 35.38641], [-80.80425, 35.3853], [-80.80437, 35.38469], [-80.80457, 35.3844], [-80.80485, 35.38417], [-80.80551, 35.38377], [-80.80646, 35.38332], [-80.80968, 35.38193], [-80.80989, 35.38181], [-80.80998, 35.38165], [-80.80999, 35.38137], [-80.80989, 35.38108], [-80.80877, 35.37857], [-80.80872, 35.37813], [-80.80881, 35.37619], [-80.80859, 35.37423], [-80.80839, 35.37338], [-80.80821, 35.37295], [-80.8077, 35.37243], [-80.80745, 35.37203], [-80.80745, 35.37203], [-80.80574, 35.37294], [-80.80313, 35.37445], [-80.7944, 35.3804], [-80.79396, 35.38059], [-80.78991, 35.38187], [-80.78948, 35.38206], [-80.78844, 35.38275], [-80.78813, 35.383], [-80.78686, 35.38424], [-80.78668, 35.38429], [-80.78668, 35.38429], [-80.78628, 35.38251], [-80.7859, 35.3803], [-80.78521, 35.37528], [-80.7847, 35.37211], [-80.78473, 35.37142], [-80.7849, 35.37091], [-80.78578, 35.36954], [-80.78578, 35.36954], [-80.78456, 35.36876], [-80.78279, 35.36772], [-80.78234, 35.36764], [-80.7814, 35.36765], [-80.78109, 35.36755], [-80.78092, 35.36737], [-80.78059, 35.36674], [-80.7802, 35.36629], [-80.78004, 35.3662], [-80.7797, 35.36612], [-80.77551, 35.36575], [-80.77443, 35.36548], [-80.77298, 35.36531], [-80.77247, 35.36538], [-80.76979, 35.36604], [-80.76935, 35.36623], [-80.76785, 35.36709], [-80.76744, 35.36714], [-80.76591, 35.36699], [-80.76548, 35.36689], [-80.76493, 35.36658], [-80.76309, 35.36576], [-80.76279, 35.36555], [-80.76265, 35.36538], [-80.76204, 35.36368], [-80.7617, 35.36315], [-80.76137, 35.3628], [-80.76105, 35.36255], [-80.76022, 35.36206], [-80.75895, 35.361], [-80.75838, 35.36025], [-80.75806, 35.35883], [-80.7575, 35.35807], [-80.75734, 35.3577], [-80.75725, 35.35719], [-80.7572, 35.35573], [-80.75711, 35.35552], [-80.7569, 35.35532], [-80.7569, 35.35532], [-80.75642, 35.35627], [-80.75584, 35.35695], [-80.75314, 35.35892], [-80.75269, 35.35911], [-80.74949, 35.35954], [-80.74949, 35.35954], [-80.74897, 35.36103], [-80.74872, 35.36212], [-80.74846, 35.36343], [-80.74844, 35.36387], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74841, 35.36441], [-80.74807, 35.36734], [-80.74768, 35.36903], [-80.74752, 35.36956], [-80.74734, 35.36994], [-80.74705, 35.37032], [-80.74668, 35.37067], [-80.74567, 35.37137], [-80.74519, 35.37161], [-80.7433, 35.3722], [-80.74276, 35.37251], [-80.74218, 35.37306], [-80.74182, 35.37368], [-80.7397, 35.37996], [-80.7396, 35.38007], [-80.7396, 35.38007], [-80.73754, 35.37989], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.72937, 35.38327], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73029, 35.39311]]}, "date": "2012-01-18", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74541, 35.41836], [-80.74572, 35.41852], [-80.74572, 35.41852], [-80.74711, 35.41763], [-80.75152, 35.41452], [-80.75224, 35.41397], [-80.75258, 35.41364], [-80.75295, 35.41311], [-80.75351, 35.41216], [-80.75563, 35.40678], [-80.75634, 35.40556], [-80.75714, 35.40466], [-80.75906, 35.40316], [-80.75998, 35.40249], [-80.76058, 35.40211], [-80.76155, 35.40157], [-80.76386, 35.40047], [-80.76612, 35.39919], [-80.76612, 35.39919], [-80.76939, 35.39733], [-80.77033, 35.39651], [-80.7737, 35.39328], [-80.7745, 35.39267], [-80.77556, 35.39213], [-80.77556, 35.39213], [-80.77503, 35.39166], [-80.77113, 35.38892], [-80.76918, 35.38759], [-80.76869, 35.38736], [-80.7676, 35.38716], [-80.76564, 35.38689], [-80.76521, 35.38686], [-80.76456, 35.3869], [-80.76378, 35.38712], [-80.76102, 35.38827], [-80.76018, 35.38853], [-80.75968, 35.38859], [-80.75884, 35.38856], [-80.75825, 35.38843], [-80.75789, 35.38828], [-80.75704, 35.38776], [-80.75671, 35.38742], [-80.7563, 35.38668], [-80.75565, 35.38518], [-80.75508, 35.38432], [-80.75413, 35.38323], [-80.75399, 35.38279], [-80.75402, 35.38243], [-80.75553, 35.37862], [-80.75573, 35.37832], [-80.75611, 35.37795], [-80.75674, 35.37759], [-80.75735, 35.3774], [-80.75805, 35.37736], [-80.75862, 35.37743], [-80.76162, 35.37853], [-80.76221, 35.37879], [-80.76516, 35.38052], [-80.76552, 35.38068], [-80.76587, 35.38077], [-80.76803, 35.38115], [-80.7685, 35.38117], [-80.76889, 35.3811], [-80.7692, 35.38099], [-80.77031, 35.38049], [-80.77078, 35.3804], [-80.77335, 35.38058], [-80.77364, 35.38058], [-80.77418, 35.38047], [-80.77461, 35.38026], [-80.77485, 35.38007], [-80.77562, 35.37919], [-80.77579, 35.37893], [-80.77588, 35.37864], [-80.7759, 35.37835], [-80.7758, 35.37791], [-80.77493, 35.37651], [-80.77405, 35.37449], [-80.77405, 35.37449], [-80.77076, 35.37531], [-80.77037, 35.37537], [-80.77002, 35.37536], [-80.76961, 35.37529], [-80.76517, 35.37399], [-80.76446, 35.37382], [-80.76381, 35.3737], [-80.76206, 35.37361], [-80.76166, 35.37353], [-80.76125, 35.37338], [-80.7582, 35.37169], [-80.75623, 35.371], [-80.75575, 35.37073], [-80.75496, 35.37006], [-80.75376, 35.36863], [-80.75176, 35.36689], [-80.74977, 35.36557], [-80.74935, 35.36524], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74841, 35.36441], [-80.74807, 35.36734], [-80.74768, 35.36903], [-80.74752, 35.36956], [-80.74734, 35.36994], [-80.74705, 35.37032], [-80.74668, 35.37067], [-80.74567, 35.37137], [-80.74519, 35.37161], [-80.7433, 35.3722], [-80.74276, 35.37251], [-80.74218, 35.37306], [-80.74182, 35.37368], [-80.7397, 35.37996], [-80.7396, 35.38007], [-80.7396, 35.38007], [-80.73754, 35.37989], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.72937, 35.38327], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73029, 35.39311]]}, "date": "2012-01-19", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.72892, 35.40821], [-80.72802, 35.40826], [-80.7229, 35.40976], [-80.7223, 35.40982], [-80.72187, 35.4098], [-80.71809, 35.4094], [-80.71772, 35.4093], [-80.71716, 35.40911], [-80.71334, 35.40745], [-80.71334, 35.40745], [-80.7117, 35.41006], [-80.71147, 35.41031], [-80.71023, 35.41139], [-80.70992, 35.41192], [-80.70977, 35.41247], [-80.70914, 35.41998], [-80.70913, 35.42079], [-80.70926, 35.42133], [-80.70952, 35.42201], [-80.71103, 35.42562], [-80.71151, 35.42623], [-80.71209, 35.42663], [-80.71266, 35.42686], [-80.71387, 35.4272], [-80.71456, 35.42759], [-80.71627, 35.42868], [-80.71766, 35.42941], [-80.718, 35.42952], [-80.71963, 35.42982], [-80.72218, 35.43024], [-80.72299, 35.43049], [-80.72349, 35.4309], [-80.7238, 35.43138], [-80.72485, 35.43371], [-80.72485, 35.43371], [-80.72694, 35.43335], [-80.7293, 35.43265], [-80.72992, 35.43235], [-80.7314, 35.43146], [-80.73579, 35.4299], [-80.73625, 35.42962], [-80.73655, 35.42924], [-80.73816, 35.42668], [-80.73895, 35.42561], [-80.73972, 35.4247], [-80.74014, 35.42439], [-80.74137, 35.42385], [-80.74173, 35.42365], [-80.74224, 35.42323], [-80.74273, 35.42268], [-80.74357, 35.42151], [-80.74438, 35.41972], [-80.74478, 35.41917], [-80.74548, 35.41865], [-80.74711, 35.41763], [-80.75152, 35.41452], [-80.75224, 35.41397], [-80.75258, 35.41364], [-80.75295, 35.41311], [-80.75351, 35.41216], [-80.75563, 35.40678], [-80.75634, 35.40556], [-80.75699, 35.40481], [-80.75744, 35.4044], [-80.75906, 35.40316], [-80.75998, 35.40249], [-80.76058, 35.40211], [-80.76155, 35.40157], [-80.76386, 35.40047], [-80.76612, 35.39919], [-80.76612, 35.39919], [-80.76939, 35.39733], [-80.77033, 35.39651], [-80.7737, 35.39328], [-80.7745, 35.39267], [-80.77556, 35.39213], [-80.77556, 35.39213], [-80.77503, 35.39166], [-80.77113, 35.38892], [-80.76918, 35.38759], [-80.76869, 35.38736], [-80.7676, 35.38716], [-80.76564, 35.38689], [-80.76521, 35.38686], [-80.76456, 35.3869], [-80.76378, 35.38712], [-80.76102, 35.38827], [-80.76018, 35.38853], [-80.75968, 35.38859], [-80.75884, 35.38856], [-80.75825, 35.38843], [-80.75789, 35.38828], [-80.75704, 35.38776], [-80.75671, 35.38742], [-80.7563, 35.38668], [-80.75565, 35.38518], [-80.75508, 35.38432], [-80.75413, 35.38323], [-80.75399, 35.38279], [-80.75402, 35.38243], [-80.75553, 35.37862], [-80.75573, 35.37832], [-80.75611, 35.37795], [-80.75674, 35.37759], [-80.75735, 35.3774], [-80.75805, 35.37736], [-80.75862, 35.37743], [-80.76162, 35.37853], [-80.76221, 35.37879], [-80.76516, 35.38052], [-80.76552, 35.38068], [-80.76587, 35.38077], [-80.76803, 35.38115], [-80.7685, 35.38117], [-80.76889, 35.3811], [-80.7692, 35.38099], [-80.77031, 35.38049], [-80.77078, 35.3804], [-80.77335, 35.38058], [-80.77364, 35.38058], [-80.77418, 35.38047], [-80.77461, 35.38026], [-80.77485, 35.38007], [-80.77562, 35.37919], [-80.77579, 35.37893], [-80.77588, 35.37864], [-80.7759, 35.37835], [-80.7758, 35.37791], [-80.77493, 35.37651], [-80.77405, 35.37449], [-80.77405, 35.37449], [-80.77076, 35.37531], [-80.77037, 35.37537], [-80.77002, 35.37536], [-80.76961, 35.37529], [-80.76517, 35.37399], [-80.76446, 35.37382], [-80.76381, 35.3737], [-80.76206, 35.37361], [-80.76166, 35.37353], [-80.76125, 35.37338], [-80.7582, 35.37169], [-80.75623, 35.371], [-80.75575, 35.37073], [-80.75496, 35.37006], [-80.75376, 35.36863], [-80.75176, 35.36689], [-80.74977, 35.36557], [-80.74935, 35.36524], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74841, 35.36441], [-80.74807, 35.36734], [-80.74768, 35.36903], [-80.74752, 35.36956], [-80.74734, 35.36994], [-80.74705, 35.37032], [-80.74668, 35.37067], [-80.74567, 35.37137], [-80.74519, 35.37161], [-80.7433, 35.3722], [-80.74276, 35.37251], [-80.74218, 35.37306], [-80.74182, 35.37368], [-80.7397, 35.37996], [-80.7396, 35.38007], [-80.7396, 35.38007], [-80.73754, 35.37989], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.72937, 35.38327], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73029, 35.39311]]}, "date": "2012-02-14", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72865, 35.40114], [-80.73028, 35.40139], [-80.73028, 35.40139], [-80.73013, 35.40186], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.72892, 35.40821], [-80.72802, 35.40826], [-80.7229, 35.40976], [-80.7223, 35.40982], [-80.72187, 35.4098], [-80.71809, 35.4094], [-80.71772, 35.4093], [-80.71716, 35.40911], [-80.71334, 35.40745], [-80.71334, 35.40745], [-80.7117, 35.41006], [-80.71147, 35.41031], [-80.71023, 35.41139], [-80.70992, 35.41192], [-80.70977, 35.41247], [-80.70914, 35.41998], [-80.70913, 35.42079], [-80.70926, 35.42133], [-80.70952, 35.42201], [-80.71103, 35.42562], [-80.71151, 35.42623], [-80.71209, 35.42663], [-80.71266, 35.42686], [-80.71387, 35.4272], [-80.71456, 35.42759], [-80.71627, 35.42868], [-80.71766, 35.42941], [-80.718, 35.42952], [-80.71963, 35.42982], [-80.72218, 35.43024], [-80.72299, 35.43049], [-80.72349, 35.4309], [-80.7238, 35.43138], [-80.72485, 35.43371], [-80.72485, 35.43371], [-80.72694, 35.43335], [-80.7293, 35.43265], [-80.72992, 35.43235], [-80.7314, 35.43146], [-80.73579, 35.4299], [-80.73609, 35.42975], [-80.73642, 35.42943], [-80.73816, 35.42668], [-80.73932, 35.42516], [-80.73972, 35.4247], [-80.73997, 35.42449], [-80.74173, 35.42365], [-80.74245, 35.42302], [-80.74357, 35.42151], [-80.74438, 35.41972], [-80.74457, 35.41942], [-80.74502, 35.41897], [-80.74572, 35.41852], [-80.74572, 35.41852], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75256, 35.42057], [-80.75294, 35.42086], [-80.7534, 35.42141], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.75628, 35.42376], [-80.75713, 35.42358], [-80.7578, 35.42352], [-80.75811, 35.42354], [-80.75839, 35.42362], [-80.75971, 35.42409], [-80.76016, 35.42421], [-80.7631, 35.42467], [-80.764, 35.42478], [-80.7644, 35.42474], [-80.76462, 35.42464], [-80.76747, 35.4226], [-80.76792, 35.42236], [-80.77201, 35.42084], [-80.77282, 35.42064], [-80.77335, 35.42061], [-80.77484, 35.42075], [-80.77551, 35.42084], [-80.77589, 35.42095], [-80.77666, 35.42129], [-80.77713, 35.42156], [-80.77754, 35.4219], [-80.77798, 35.4225], [-80.77821, 35.42269], [-80.77896, 35.42292], [-80.78116, 35.42347], [-80.78176, 35.42352], [-80.78718, 35.4232], [-80.7875, 35.42319], [-80.7879, 35.42324], [-80.78822, 35.42335], [-80.79105, 35.42488], [-80.79201, 35.42529], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79428, 35.42758], [-80.7996, 35.43312], [-80.80167, 35.43512], [-80.80274, 35.43638], [-80.80293, 35.43653], [-80.80293, 35.43653], [-80.80302, 35.43527], [-80.80308, 35.43501], [-80.80358, 35.43395], [-80.80358, 35.43395], [-80.80441, 35.43388], [-80.80532, 35.43386], [-80.8058, 35.43394], [-80.80696, 35.43446], [-80.80764, 35.43509], [-80.80809, 35.43541], [-80.80834, 35.43552], [-80.80873, 35.43561], [-80.80952, 35.43566], [-80.81239, 35.43564], [-80.81266, 35.43567], [-80.81266, 35.43567], [-80.81127, 35.43767], [-80.81108, 35.43807], [-80.81098, 35.43885], [-80.80997, 35.44059], [-80.80915, 35.44185], [-80.80893, 35.44234], [-80.80882, 35.4434], [-80.80933, 35.44579], [-80.80935, 35.44614], [-80.8092, 35.44677], [-80.80843, 35.44883], [-80.8083, 35.44934], [-80.80835, 35.44964], [-80.80901, 35.45134], [-80.8091, 35.4517], [-80.80903, 35.45212], [-80.80875, 35.45268], [-80.80874, 35.45302], [-80.80885, 35.45328], [-80.80916, 35.4537], [-80.80954, 35.45441], [-80.80954, 35.45441], [-80.80772, 35.45507], [-80.80669, 35.45531], [-80.80605, 35.45539], [-80.80495, 35.45541], [-80.80432, 35.45535], [-80.80341, 35.45516], [-80.80252, 35.45486], [-80.80252, 35.45486], [-80.8025, 35.45534], [-80.80265, 35.45563], [-80.80657, 35.46], [-80.80844, 35.46219], [-80.80902, 35.46273], [-80.80942, 35.46303], [-80.80942, 35.46303], [-80.81063, 35.46189], [-80.811, 35.46123], [-80.81134, 35.46102], [-80.81134, 35.46102], [-80.81123, 35.46046], [-80.81135, 35.46011], [-80.81223, 35.45797], [-80.81258, 35.45755], [-80.81258, 35.45755], [-80.81242, 35.45739], [-80.81233, 35.45718], [-80.81234, 35.45693], [-80.81234, 35.45693], [-80.81366, 35.45714], [-80.8142, 35.45713], [-80.81458, 35.45705], [-80.81492, 35.45692], [-80.81603, 35.45623], [-80.81629, 35.45612], [-80.81688, 35.45599], [-80.81723, 35.45598], [-80.81792, 35.45611], [-80.82114, 35.45733], [-80.82114, 35.45733], [-80.8208, 35.4589], [-80.82082, 35.4592], [-80.82105, 35.4603], [-80.82105, 35.4603], [-80.82068, 35.46043], [-80.82021, 35.46069], [-80.81938, 35.46116], [-80.81887, 35.46152], [-80.81849, 35.46206], [-80.81807, 35.46337], [-80.81791, 35.46372], [-80.81764, 35.46405], [-80.81719, 35.46437], [-80.81613, 35.46482], [-80.81566, 35.4651], [-80.81522, 35.46547], [-80.81418, 35.46665], [-80.81418, 35.46665], [-80.81437, 35.46702], [-80.81487, 35.46838], [-80.81506, 35.46871], [-80.81576, 35.46964], [-80.81604, 35.47019], [-80.81639, 35.4716], [-80.8167, 35.47246], [-80.81916, 35.47674], [-80.81968, 35.47795], [-80.81995, 35.47924], [-80.82006, 35.48224], [-80.82005, 35.4834], [-80.81982, 35.48385], [-80.81982, 35.48385], [-80.81967, 35.48388], [-80.8196, 35.48397], [-80.81967, 35.48412], [-80.81989, 35.48414], [-80.82095, 35.48472], [-80.83665, 35.49444], [-80.83693, 35.49456], [-80.83929, 35.49529], [-80.83929, 35.49529], [-80.83579, 35.49635], [-80.83497, 35.4967], [-80.83449, 35.49699], [-80.83376, 35.49762], [-80.83275, 35.49892], [-80.83244, 35.49922], [-80.8319, 35.49951], [-80.83143, 35.49963], [-80.83063, 35.49969], [-80.82932, 35.49966], [-80.82524, 35.49937], [-80.82345, 35.49911], [-80.82134, 35.49868], [-80.8207, 35.49862], [-80.81988, 35.49867], [-80.81779, 35.49892], [-80.81707, 35.49906], [-80.81673, 35.49921], [-80.81624, 35.49952], [-80.81427, 35.50093], [-80.81407, 35.50119], [-80.81354, 35.5025], [-80.81334, 35.5028], [-80.81195, 35.50398], [-80.81154, 35.50425], [-80.81123, 35.50437], [-80.80967, 35.50476], [-80.80882, 35.50508], [-80.80849, 35.50529], [-80.80778, 35.50586], [-80.8074, 35.50604], [-80.80677, 35.50617], [-80.80613, 35.5062], [-80.80562, 35.50618], [-80.80463, 35.50601], [-80.80394, 35.50604], [-80.80167, 35.50667], [-80.80149, 35.50679], [-80.80125, 35.50708], [-80.80125, 35.50708], [-80.80087, 35.50758], [-80.80066, 35.50769], [-80.7985, 35.50743], [-80.79747, 35.50735], [-80.79725, 35.50738], [-80.79725, 35.50738], [-80.79666, 35.50716], [-80.79666, 35.50716], [-80.79549, 35.50658], [-80.79503, 35.50628], [-80.79477, 35.50604], [-80.79454, 35.50565], [-80.79431, 35.50461], [-80.79435, 35.50407], [-80.79449, 35.50359], [-80.79599, 35.5009], [-80.79602, 35.50063], [-80.79587, 35.49837], [-80.79592, 35.49782], [-80.7962, 35.49629], [-80.79621, 35.49579], [-80.79609, 35.49521], [-80.79582, 35.4946], [-80.79562, 35.4943], [-80.7953, 35.49395], [-80.7917, 35.49097], [-80.79134, 35.49062], [-80.79114, 35.49034], [-80.79096, 35.48994], [-80.79088, 35.48957], [-80.7909, 35.48831], [-80.79088, 35.48799], [-80.79077, 35.48762], [-80.79059, 35.48729], [-80.78968, 35.48607], [-80.78953, 35.4857], [-80.78947, 35.48534], [-80.78954, 35.48437], [-80.78949, 35.48391], [-80.78938, 35.48364], [-80.78921, 35.48339], [-80.7884, 35.48261], [-80.78812, 35.48223], [-80.78655, 35.47896], [-80.7864, 35.47851], [-80.78519, 35.47351], [-80.78513, 35.47345], [-80.78513, 35.47345], [-80.78169, 35.47508], [-80.78129, 35.47516], [-80.78021, 35.47508], [-80.78021, 35.47508], [-80.77555, 35.47452], [-80.77496, 35.47451], [-80.77466, 35.47456], [-80.77385, 35.47479], [-80.77338, 35.47499], [-80.77129, 35.47627], [-80.77063, 35.47675], [-80.77051, 35.47695], [-80.77043, 35.47728], [-80.77029, 35.48141], [-80.77014, 35.48202], [-80.76692, 35.48734], [-80.76471, 35.49024], [-80.76471, 35.49024], [-80.76001, 35.48856], [-80.76001, 35.48856], [-80.75731, 35.48316], [-80.75679, 35.48229], [-80.75605, 35.48133], [-80.75522, 35.48055], [-80.75278, 35.47857], [-80.75134, 35.47748], [-80.75016, 35.47668], [-80.75016, 35.47668], [-80.75619, 35.46857], [-80.75633, 35.46824], [-80.75738, 35.46478], [-80.75779, 35.46275], [-80.75855, 35.46121], [-80.75922, 35.45922], [-80.75932, 35.45819], [-80.75939, 35.45793], [-80.75959, 35.45762], [-80.75998, 35.45718], [-80.76115, 35.45618], [-80.76173, 35.45558], [-80.76209, 35.4551], [-80.76246, 35.45445], [-80.76264, 35.45402], [-80.76308, 35.45271], [-80.76371, 35.45188], [-80.76393, 35.4515], [-80.76402, 35.45106], [-80.76409, 35.44955], [-80.76409, 35.44928], [-80.76397, 35.44856], [-80.76401, 35.44829], [-80.76418, 35.44801], [-80.76487, 35.44716], [-80.76568, 35.44642], [-80.76596, 35.44596], [-80.76602, 35.44555], [-80.76597, 35.44519], [-80.7658, 35.4448], [-80.76426, 35.44283], [-80.76401, 35.44217], [-80.76386, 35.44109], [-80.76382, 35.4401], [-80.76392, 35.43969], [-80.76442, 35.43866], [-80.76442, 35.43866], [-80.76452, 35.4382], [-80.76453, 35.43788], [-80.76441, 35.4362], [-80.76434, 35.43565], [-80.76412, 35.43503], [-80.76379, 35.43464], [-80.76308, 35.43413], [-80.76279, 35.43377], [-80.76265, 35.43332], [-80.76266, 35.43278], [-80.76261, 35.43246], [-80.76251, 35.43225], [-80.76222, 35.43187], [-80.7615, 35.43114], [-80.7609, 35.43069], [-80.76035, 35.43043], [-80.75977, 35.43028], [-80.75977, 35.43028], [-80.75928, 35.43016], [-80.75893, 35.42995], [-80.75726, 35.42794], [-80.75352, 35.42159], [-80.75314, 35.42107], [-80.75277, 35.42072], [-80.75208, 35.42031], [-80.75159, 35.42013], [-80.74601, 35.41862], [-80.74558, 35.41847], [-80.74309, 35.41645], [-80.74092, 35.4145], [-80.73984, 35.41359], [-80.73526, 35.41053], [-80.73379, 35.40906], [-80.73343, 35.40876], [-80.73307, 35.40855], [-80.73263, 35.40842], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73068, 35.40803], [-80.73004, 35.40709], [-80.72961, 35.40628], [-80.72908, 35.40393], [-80.7291, 35.40344], [-80.72923, 35.40296], [-80.72939, 35.40268], [-80.73013, 35.40186], [-80.73028, 35.40139], [-80.73028, 35.40139], [-80.72925, 35.40123]]}, "date": "2012-02-15", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.72892, 35.40821], [-80.72802, 35.40826], [-80.7229, 35.40976], [-80.7223, 35.40982], [-80.72187, 35.4098], [-80.71809, 35.4094], [-80.71772, 35.4093], [-80.71716, 35.40911], [-80.71334, 35.40745], [-80.71334, 35.40745], [-80.7117, 35.41006], [-80.71147, 35.41031], [-80.71023, 35.41139], [-80.70992, 35.41192], [-80.70977, 35.41247], [-80.70914, 35.41998], [-80.70913, 35.42079], [-80.70926, 35.42133], [-80.70952, 35.42201], [-80.71103, 35.42562], [-80.71151, 35.42623], [-80.71209, 35.42663], [-80.71266, 35.42686], [-80.71387, 35.4272], [-80.71456, 35.42759], [-80.71627, 35.42868], [-80.71766, 35.42941], [-80.718, 35.42952], [-80.71963, 35.42982], [-80.72218, 35.43024], [-80.72299, 35.43049], [-80.72349, 35.4309], [-80.7238, 35.43138], [-80.72709, 35.43871], [-80.72779, 35.44124], [-80.72829, 35.44222], [-80.72956, 35.44443], [-80.72981, 35.44498], [-80.73005, 35.44569], [-80.73116, 35.45106], [-80.73178, 35.45356], [-80.73204, 35.455], [-80.73232, 35.4599], [-80.73239, 35.46039], [-80.73254, 35.46055], [-80.73293, 35.46071], [-80.73605, 35.46174], [-80.73723, 35.46219], [-80.73826, 35.46288], [-80.73887, 35.46342], [-80.7408, 35.46627], [-80.74394, 35.47036], [-80.74646, 35.47374], [-80.74705, 35.47439], [-80.7483, 35.4754], [-80.75185, 35.47785], [-80.75459, 35.48002], [-80.75561, 35.48089], [-80.75605, 35.48133], [-80.75679, 35.48229], [-80.75731, 35.48316], [-80.76001, 35.48856], [-80.76001, 35.48856], [-80.76471, 35.49024], [-80.76471, 35.49024], [-80.76692, 35.48734], [-80.77014, 35.48202], [-80.77029, 35.48141], [-80.77043, 35.47728], [-80.77051, 35.47695], [-80.77063, 35.47675], [-80.77129, 35.47627], [-80.77338, 35.47499], [-80.77385, 35.47479], [-80.77466, 35.47456], [-80.77496, 35.47451], [-80.77555, 35.47452], [-80.78021, 35.47508], [-80.78021, 35.47508], [-80.78108, 35.47516], [-80.7814, 35.47515], [-80.78169, 35.47508], [-80.78257, 35.47469], [-80.78695, 35.47255], [-80.78738, 35.47238], [-80.78884, 35.47203], [-80.78955, 35.47199], [-80.79018, 35.47206], [-80.79672, 35.47415], [-80.79748, 35.47426], [-80.7998, 35.47442], [-80.8005, 35.47461], [-80.80099, 35.47483], [-80.80147, 35.47516], [-80.80192, 35.47563], [-80.80515, 35.48001], [-80.80552, 35.48044], [-80.80593, 35.48074], [-80.80689, 35.48129], [-80.80988, 35.48296], [-80.81042, 35.48322], [-80.81114, 35.48342], [-80.81157, 35.48346], [-80.81796, 35.48335], [-80.81835, 35.48338], [-80.81883, 35.48349], [-80.81934, 35.48377], [-80.8196, 35.48397], [-80.8196, 35.48397], [-80.81962, 35.48407], [-80.81972, 35.48414], [-80.81984, 35.48416], [-80.81994, 35.4841], [-80.81997, 35.48401], [-80.81992, 35.4839], [-80.82006, 35.48347], [-80.82004, 35.48142], [-80.81995, 35.47924], [-80.81968, 35.47795], [-80.81916, 35.47674], [-80.8167, 35.47246], [-80.81639, 35.4716], [-80.81604, 35.47019], [-80.81576, 35.46964], [-80.81506, 35.46871], [-80.81487, 35.46838], [-80.81437, 35.46702], [-80.81418, 35.46665], [-80.81418, 35.46665], [-80.81522, 35.46547], [-80.81566, 35.4651], [-80.81613, 35.46482], [-80.81719, 35.46437], [-80.81764, 35.46405], [-80.81791, 35.46372], [-80.81807, 35.46337], [-80.81849, 35.46206], [-80.81887, 35.46152], [-80.81938, 35.46116], [-80.82021, 35.46069], [-80.82068, 35.46043], [-80.82105, 35.4603], [-80.82105, 35.4603], [-80.82082, 35.4592], [-80.8208, 35.4589], [-80.82114, 35.45733], [-80.82114, 35.45733], [-80.82191, 35.45736], [-80.82235, 35.45729], [-80.82292, 35.4571], [-80.82346, 35.45677], [-80.8248, 35.45557], [-80.82569, 35.45508], [-80.82569, 35.45508], [-80.82519, 35.45428], [-80.82478, 35.45344], [-80.82468, 35.4531], [-80.82448, 35.45284], [-80.82427, 35.45275], [-80.82243, 35.45225], [-80.82106, 35.45175], [-80.82032, 35.45139], [-80.81947, 35.45075], [-80.81947, 35.45075], [-80.80954, 35.45441], [-80.80954, 35.45441], [-80.80916, 35.4537], [-80.80885, 35.45328], [-80.80874, 35.45302], [-80.80875, 35.45268], [-80.80903, 35.45212], [-80.8091, 35.4517], [-80.80901, 35.45134], [-80.80835, 35.44964], [-80.8083, 35.44934], [-80.80843, 35.44883], [-80.8092, 35.44677], [-80.80935, 35.44614], [-80.80933, 35.44579], [-80.80882, 35.4434], [-80.80893, 35.44234], [-80.80915, 35.44185], [-80.80997, 35.44059], [-80.81098, 35.43885], [-80.81108, 35.43807], [-80.81127, 35.43767], [-80.81266, 35.43567], [-80.81266, 35.43567], [-80.81239, 35.43564], [-80.80952, 35.43566], [-80.80873, 35.43561], [-80.80834, 35.43552], [-80.80809, 35.43541], [-80.80764, 35.43509], [-80.80696, 35.43446], [-80.8058, 35.43394], [-80.80532, 35.43386], [-80.80441, 35.43388], [-80.80358, 35.43395], [-80.80358, 35.43395], [-80.80308, 35.43501], [-80.80302, 35.43527], [-80.80293, 35.43653], [-80.80293, 35.43653], [-80.80274, 35.43638], [-80.80167, 35.43512], [-80.7996, 35.43312], [-80.79428, 35.42758], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79406, 35.42406], [-80.79483, 35.42324], [-80.79732, 35.42077], [-80.79863, 35.41961], [-80.79885, 35.41926], [-80.80057, 35.41562], [-80.80089, 35.41512], [-80.80186, 35.41438], [-80.8022, 35.41425], [-80.80461, 35.41366], [-80.80461, 35.41366], [-80.80471, 35.41173], [-80.80469, 35.41138], [-80.80462, 35.41114], [-80.80437, 35.41073], [-80.80356, 35.40982], [-80.80334, 35.40933], [-80.8031, 35.40852], [-80.80282, 35.40789], [-80.80251, 35.40739], [-80.80203, 35.40691], [-80.80179, 35.40653], [-80.80133, 35.4049], [-80.80126, 35.40431], [-80.80129, 35.40417], [-80.80147, 35.40387], [-80.80186, 35.40341], [-80.80349, 35.40201], [-80.80366, 35.40169], [-80.80368, 35.40065], [-80.8035, 35.3993], [-80.80238, 35.39644], [-80.80226, 35.39591], [-80.80234, 35.39557], [-80.80272, 35.3948], [-80.80313, 35.39375], [-80.80431, 35.39016], [-80.80459, 35.38905], [-80.80465, 35.38868], [-80.80463, 35.38838], [-80.80426, 35.38685], [-80.8042, 35.38641], [-80.80425, 35.3853], [-80.80437, 35.38469], [-80.80457, 35.3844], [-80.80485, 35.38417], [-80.80551, 35.38377], [-80.80646, 35.38332], [-80.80968, 35.38193], [-80.80989, 35.38181], [-80.80998, 35.38165], [-80.80999, 35.38137], [-80.80989, 35.38108], [-80.80877, 35.37857], [-80.80872, 35.37813], [-80.80881, 35.37619], [-80.80859, 35.37423], [-80.80839, 35.37338], [-80.80821, 35.37295], [-80.8077, 35.37243], [-80.80745, 35.37203], [-80.80745, 35.37203], [-80.80574, 35.37294], [-80.80313, 35.37445], [-80.7944, 35.3804], [-80.79396, 35.38059], [-80.78991, 35.38187], [-80.78948, 35.38206], [-80.78844, 35.38275], [-80.78813, 35.383], [-80.78686, 35.38424], [-80.78668, 35.38429], [-80.78668, 35.38429], [-80.78628, 35.38251], [-80.7859, 35.3803], [-80.78542, 35.37673], [-80.78482, 35.37283], [-80.78482, 35.37283], [-80.78339, 35.37296], [-80.78295, 35.3729], [-80.7817, 35.37251], [-80.78139, 35.37247], [-80.78075, 35.37251], [-80.77909, 35.37297], [-80.77405, 35.37449], [-80.77076, 35.37531], [-80.77037, 35.37537], [-80.77002, 35.37536], [-80.76961, 35.37529], [-80.76517, 35.37399], [-80.76413, 35.37375], [-80.76381, 35.3737], [-80.76241, 35.37365], [-80.76186, 35.37358], [-80.76125, 35.37338], [-80.76079, 35.37315], [-80.7582, 35.37169], [-80.75623, 35.371], [-80.75575, 35.37073], [-80.75496, 35.37006], [-80.75376, 35.36863], [-80.75176, 35.36689], [-80.74977, 35.36557], [-80.74935, 35.36524], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74841, 35.36441], [-80.74807, 35.36734], [-80.74768, 35.36903], [-80.74752, 35.36956], [-80.74734, 35.36994], [-80.74705, 35.37032], [-80.74668, 35.37067], [-80.74567, 35.37137], [-80.74519, 35.37161], [-80.7433, 35.3722], [-80.74276, 35.37251], [-80.74218, 35.37306], [-80.74182, 35.37368], [-80.7397, 35.37996], [-80.7396, 35.38007], [-80.7396, 35.38007], [-80.73754, 35.37989], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.72937, 35.38327], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73029, 35.39311]]}, "date": "2012-02-22", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72939, 35.40268], [-80.72923, 35.40296], [-80.72912, 35.40332], [-80.72908, 35.40393], [-80.72956, 35.40612], [-80.72976, 35.40661], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75277, 35.42072], [-80.75314, 35.42107], [-80.75352, 35.42159], [-80.75726, 35.42794], [-80.75893, 35.42995], [-80.75928, 35.43016], [-80.75977, 35.43028], [-80.75977, 35.43028], [-80.76035, 35.43043], [-80.7609, 35.43069], [-80.7615, 35.43114], [-80.76222, 35.43187], [-80.76251, 35.43225], [-80.76261, 35.43246], [-80.76266, 35.43278], [-80.76265, 35.43332], [-80.76279, 35.43377], [-80.76308, 35.43413], [-80.76379, 35.43464], [-80.76412, 35.43503], [-80.76434, 35.43565], [-80.76441, 35.4362], [-80.76453, 35.43788], [-80.76452, 35.4382], [-80.76442, 35.43866], [-80.76442, 35.43866], [-80.76392, 35.43969], [-80.76382, 35.4401], [-80.76386, 35.44109], [-80.76401, 35.44217], [-80.76426, 35.44283], [-80.7658, 35.4448], [-80.76597, 35.44519], [-80.76602, 35.44555], [-80.76596, 35.44596], [-80.76568, 35.44642], [-80.76487, 35.44716], [-80.76418, 35.44801], [-80.76401, 35.44829], [-80.76397, 35.44856], [-80.76409, 35.44928], [-80.76409, 35.44955], [-80.76402, 35.45106], [-80.76393, 35.4515], [-80.76371, 35.45188], [-80.76308, 35.45271], [-80.76264, 35.45402], [-80.76246, 35.45445], [-80.76209, 35.4551], [-80.76173, 35.45558], [-80.76115, 35.45618], [-80.75998, 35.45718], [-80.75959, 35.45762], [-80.75939, 35.45793], [-80.75932, 35.45819], [-80.75922, 35.45922], [-80.75855, 35.46121], [-80.75779, 35.46275], [-80.75738, 35.46478], [-80.75633, 35.46824], [-80.75619, 35.46857], [-80.75016, 35.47668], [-80.75016, 35.47668], [-80.75134, 35.47748], [-80.75278, 35.47857], [-80.75522, 35.48055], [-80.75605, 35.48133], [-80.75679, 35.48229], [-80.75731, 35.48316], [-80.76001, 35.48856], [-80.76001, 35.48856], [-80.76517, 35.49041], [-80.76572, 35.49067], [-80.7662, 35.491], [-80.76651, 35.49136], [-80.7671, 35.49226], [-80.76945, 35.49601], [-80.76978, 35.49696], [-80.76988, 35.49772], [-80.76987, 35.49827], [-80.76962, 35.49962], [-80.76828, 35.50577], [-80.76828, 35.50577], [-80.76784, 35.50572], [-80.76696, 35.50572], [-80.76665, 35.50564], [-80.76604, 35.50536], [-80.76515, 35.50473], [-80.76515, 35.50473], [-80.76363, 35.50491], [-80.76221, 35.50463], [-80.75921, 35.50449], [-80.75676, 35.50429], [-80.75495, 35.50421], [-80.7485, 35.50408], [-80.74658, 35.50398], [-80.74343, 35.50363], [-80.73984, 35.50309], [-80.7382, 35.50292], [-80.73779, 35.50296], [-80.73746, 35.50308], [-80.73623, 35.50409], [-80.73578, 35.50431], [-80.73505, 35.50441], [-80.73408, 35.50447], [-80.73284, 35.50443], [-80.73131, 35.50429], [-80.73084, 35.5043], [-80.73035, 35.50441], [-80.72601, 35.50576], [-80.72345, 35.50684], [-80.72293, 35.50702], [-80.72227, 35.50708], [-80.72087, 35.50687], [-80.72087, 35.50687], [-80.72089, 35.50378], [-80.72089, 35.50378], [-80.71963, 35.50357], [-80.71913, 35.50331], [-80.7189, 35.50307], [-80.71823, 35.50189], [-80.71715, 35.49891], [-80.71679, 35.49806], [-80.716, 35.49678], [-80.71562, 35.49632], [-80.71295, 35.49401], [-80.71181, 35.49295], [-80.71106, 35.49243], [-80.71062, 35.49218], [-80.71021, 35.49201], [-80.70834, 35.49153], [-80.7068, 35.49082], [-80.7068, 35.49082], [-80.70665, 35.49214], [-80.70653, 35.49275], [-80.70589, 35.49429], [-80.70537, 35.49572], [-80.70446, 35.49884], [-80.70442, 35.49917], [-80.70454, 35.49994], [-80.70454, 35.50027], [-80.70434, 35.50056], [-80.704, 35.50081], [-80.70328, 35.50121], [-80.70289, 35.50136], [-80.69995, 35.50199], [-80.69934, 35.50197], [-80.69807, 35.5016], [-80.69739, 35.50146], [-80.69669, 35.50147], [-80.69601, 35.50156], [-80.69414, 35.50191], [-80.69014, 35.50283], [-80.68501, 35.50429], [-80.68501, 35.50429], [-80.68404, 35.50083], [-80.6834, 35.49892], [-80.68315, 35.49846], [-80.68286, 35.49815], [-80.68243, 35.49797], [-80.68105, 35.49786], [-80.68022, 35.49774], [-80.67999, 35.49766], [-80.67965, 35.49748], [-80.67903, 35.49702], [-80.67858, 35.49657], [-80.67829, 35.49577], [-80.67802, 35.4946], [-80.67797, 35.494], [-80.67794, 35.49136], [-80.67789, 35.49096], [-80.67789, 35.49096], [-80.67733, 35.49105], [-80.67663, 35.49108], [-80.67561, 35.49098], [-80.67168, 35.49], [-80.67168, 35.49], [-80.67193, 35.48881], [-80.67378, 35.48357], [-80.67397, 35.48285], [-80.67408, 35.48202], [-80.67403, 35.48143], [-80.67306, 35.47712], [-80.67283, 35.47643], [-80.67129, 35.47322], [-80.67086, 35.47243], [-80.67069, 35.47194], [-80.67059, 35.47145], [-80.67042, 35.46914], [-80.67025, 35.46847], [-80.67002, 35.46791], [-80.66966, 35.46734], [-80.66882, 35.46621], [-80.66809, 35.46536], [-80.6676, 35.46466], [-80.66645, 35.46323], [-80.66569, 35.46239], [-80.66468, 35.4611], [-80.66421, 35.46035], [-80.66399, 35.45983], [-80.66387, 35.45938], [-80.66381, 35.45877], [-80.66384, 35.45828], [-80.66398, 35.45758], [-80.66446, 35.4566], [-80.66487, 35.45602], [-80.66487, 35.45602], [-80.66888, 35.45238], [-80.67001, 35.45124], [-80.67303, 35.44843], [-80.67385, 35.44772], [-80.67667, 35.44501], [-80.67812, 35.44324], [-80.67902, 35.44175], [-80.67947, 35.44081], [-80.68004, 35.4394], [-80.68025, 35.43848], [-80.68028, 35.43818], [-80.68024, 35.43811], [-80.68043, 35.43723], [-80.6805, 35.43642], [-80.68052, 35.43498], [-80.68034, 35.43314], [-80.67877, 35.42615], [-80.67857, 35.4255], [-80.67736, 35.42331], [-80.67736, 35.42318], [-80.67555, 35.42096], [-80.67481, 35.42028], [-80.6742, 35.41983], [-80.67388, 35.41964], [-80.67375, 35.41959], [-80.67364, 35.41961], [-80.67303, 35.41927], [-80.67303, 35.41927], [-80.67148, 35.41836], [-80.67027, 35.41757], [-80.66937, 35.41675], [-80.6681, 35.41544], [-80.66777, 35.41504], [-80.66725, 35.41419], [-80.66726, 35.41411], [-80.66689, 35.41345], [-80.66624, 35.41238], [-80.66416, 35.40868], [-80.66198, 35.40464], [-80.66112, 35.40325], [-80.66048, 35.40198], [-80.65988, 35.40052], [-80.65931, 35.39875], [-80.65918, 35.39807], [-80.65918, 35.39807], [-80.66011, 35.3979], [-80.66183, 35.39739], [-80.66299, 35.39702], [-80.66346, 35.3968], [-80.66616, 35.39495], [-80.66651, 35.39475], [-80.66732, 35.39439], [-80.66854, 35.39393], [-80.66893, 35.39372], [-80.67052, 35.39243], [-80.67232, 35.39114], [-80.67232, 35.39114], [-80.67352, 35.39245], [-80.6738, 35.39284], [-80.67421, 35.39406], [-80.67429, 35.39459], [-80.67429, 35.39459], [-80.675, 35.39463], [-80.67537, 35.39478], [-80.67561, 35.395], [-80.67596, 35.39588], [-80.67648, 35.39668], [-80.67666, 35.39729], [-80.67666, 35.39729], [-80.67595, 35.39754]]}, "date": "2012-02-23", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.72892, 35.40821], [-80.72802, 35.40826], [-80.7229, 35.40976], [-80.7223, 35.40982], [-80.72187, 35.4098], [-80.71809, 35.4094], [-80.71772, 35.4093], [-80.71716, 35.40911], [-80.71334, 35.40745], [-80.71334, 35.40745], [-80.7117, 35.41006], [-80.71147, 35.41031], [-80.71023, 35.41139], [-80.70992, 35.41192], [-80.70977, 35.41247], [-80.70914, 35.41998], [-80.70913, 35.42079], [-80.70926, 35.42133], [-80.70952, 35.42201], [-80.71103, 35.42562], [-80.71151, 35.42623], [-80.71209, 35.42663], [-80.71266, 35.42686], [-80.71387, 35.4272], [-80.71456, 35.42759], [-80.71627, 35.42868], [-80.71766, 35.42941], [-80.718, 35.42952], [-80.71963, 35.42982], [-80.72218, 35.43024], [-80.72299, 35.43049], [-80.72349, 35.4309], [-80.7238, 35.43138], [-80.72485, 35.43371], [-80.72485, 35.43371], [-80.72694, 35.43335], [-80.7293, 35.43265], [-80.72992, 35.43235], [-80.7314, 35.43146], [-80.73579, 35.4299], [-80.73609, 35.42975], [-80.73642, 35.42943], [-80.73816, 35.42668], [-80.73932, 35.42516], [-80.73972, 35.4247], [-80.73997, 35.42449], [-80.74173, 35.42365], [-80.74245, 35.42302], [-80.74357, 35.42151], [-80.74438, 35.41972], [-80.74457, 35.41942], [-80.74502, 35.41897], [-80.74572, 35.41852], [-80.74572, 35.41852], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75256, 35.42057], [-80.75294, 35.42086], [-80.7534, 35.42141], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.75628, 35.42376], [-80.75713, 35.42358], [-80.7578, 35.42352], [-80.75811, 35.42354], [-80.75839, 35.42362], [-80.75971, 35.42409], [-80.76016, 35.42421], [-80.7631, 35.42467], [-80.764, 35.42478], [-80.7644, 35.42474], [-80.76462, 35.42464], [-80.76747, 35.4226], [-80.76792, 35.42236], [-80.77201, 35.42084], [-80.77282, 35.42064], [-80.77335, 35.42061], [-80.77484, 35.42075], [-80.77551, 35.42084], [-80.77589, 35.42095], [-80.77666, 35.42129], [-80.77713, 35.42156], [-80.77754, 35.4219], [-80.77798, 35.4225], [-80.77821, 35.42269], [-80.77896, 35.42292], [-80.78116, 35.42347], [-80.78176, 35.42352], [-80.78718, 35.4232], [-80.7875, 35.42319], [-80.7879, 35.42324], [-80.78822, 35.42335], [-80.79105, 35.42488], [-80.79201, 35.42529], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79406, 35.42406], [-80.79483, 35.42324], [-80.79732, 35.42077], [-80.79863, 35.41961], [-80.79885, 35.41926], [-80.80057, 35.41562], [-80.80089, 35.41512], [-80.80186, 35.41438], [-80.8022, 35.41425], [-80.80461, 35.41366], [-80.80461, 35.41366], [-80.80471, 35.41173], [-80.80469, 35.41138], [-80.80462, 35.41114], [-80.80437, 35.41073], [-80.80356, 35.40982], [-80.80334, 35.40933], [-80.8031, 35.40852], [-80.80282, 35.40789], [-80.80251, 35.40739], [-80.80203, 35.40691], [-80.80179, 35.40653], [-80.80133, 35.4049], [-80.80126, 35.40431], [-80.80129, 35.40417], [-80.80147, 35.40387], [-80.80186, 35.40341], [-80.80349, 35.40201], [-80.80366, 35.40169], [-80.80368, 35.40065], [-80.8035, 35.3993], [-80.80238, 35.39644], [-80.80226, 35.39591], [-80.80234, 35.39557], [-80.80272, 35.3948], [-80.80313, 35.39375], [-80.80431, 35.39016], [-80.80459, 35.38905], [-80.80465, 35.38868], [-80.80463, 35.38838], [-80.80426, 35.38685], [-80.8042, 35.38641], [-80.80425, 35.3853], [-80.80437, 35.38469], [-80.80457, 35.3844], [-80.80485, 35.38417], [-80.80551, 35.38377], [-80.80646, 35.38332], [-80.80968, 35.38193], [-80.80989, 35.38181], [-80.80998, 35.38165], [-80.80999, 35.38137], [-80.80989, 35.38108], [-80.80877, 35.37857], [-80.80872, 35.37813], [-80.80881, 35.37619], [-80.80859, 35.37423], [-80.80839, 35.37338], [-80.80821, 35.37295], [-80.8077, 35.37243], [-80.80745, 35.37203], [-80.80745, 35.37203], [-80.80574, 35.37294], [-80.80313, 35.37445], [-80.7944, 35.3804], [-80.79396, 35.38059], [-80.78977, 35.38192], [-80.78905, 35.38232], [-80.78813, 35.383], [-80.78686, 35.38424], [-80.78668, 35.38429], [-80.78586, 35.38513], [-80.78416, 35.3867], [-80.77943, 35.39042], [-80.7786, 35.39092], [-80.77556, 35.39213], [-80.77556, 35.39213], [-80.77503, 35.39166], [-80.77113, 35.38892], [-80.76918, 35.38759], [-80.76869, 35.38736], [-80.7676, 35.38716], [-80.76564, 35.38689], [-80.76521, 35.38686], [-80.76456, 35.3869], [-80.76378, 35.38712], [-80.76102, 35.38827], [-80.76018, 35.38853], [-80.75968, 35.38859], [-80.75884, 35.38856], [-80.75825, 35.38843], [-80.75789, 35.38828], [-80.75704, 35.38776], [-80.75671, 35.38742], [-80.7563, 35.38668], [-80.75565, 35.38518], [-80.75508, 35.38432], [-80.75413, 35.38323], [-80.75399, 35.38279], [-80.75402, 35.38243], [-80.75553, 35.37862], [-80.75573, 35.37832], [-80.75611, 35.37795], [-80.75674, 35.37759], [-80.75735, 35.3774], [-80.75805, 35.37736], [-80.75862, 35.37743], [-80.76162, 35.37853], [-80.76221, 35.37879], [-80.76516, 35.38052], [-80.76552, 35.38068], [-80.76587, 35.38077], [-80.76803, 35.38115], [-80.7685, 35.38117], [-80.76889, 35.3811], [-80.7692, 35.38099], [-80.77031, 35.38049], [-80.77078, 35.3804], [-80.77335, 35.38058], [-80.77364, 35.38058], [-80.77418, 35.38047], [-80.77461, 35.38026], [-80.77485, 35.38007], [-80.77562, 35.37919], [-80.77579, 35.37893], [-80.77588, 35.37864], [-80.7759, 35.37835], [-80.7758, 35.37791], [-80.77493, 35.37651], [-80.77405, 35.37449], [-80.77405, 35.37449], [-80.77076, 35.37531], [-80.77037, 35.37537], [-80.77002, 35.37536], [-80.76961, 35.37529], [-80.76517, 35.37399], [-80.76446, 35.37382], [-80.76381, 35.3737], [-80.76206, 35.37361], [-80.76166, 35.37353], [-80.76125, 35.37338], [-80.7582, 35.37169], [-80.75623, 35.371], [-80.75575, 35.37073], [-80.75496, 35.37006], [-80.75376, 35.36863], [-80.75176, 35.36689], [-80.74977, 35.36557], [-80.74935, 35.36524], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74841, 35.36441], [-80.74807, 35.36734], [-80.74768, 35.36903], [-80.74752, 35.36956], [-80.74734, 35.36994], [-80.74705, 35.37032], [-80.74668, 35.37067], [-80.74567, 35.37137], [-80.74519, 35.37161], [-80.7433, 35.3722], [-80.74276, 35.37251], [-80.74218, 35.37306], [-80.74182, 35.37368], [-80.7397, 35.37996], [-80.7396, 35.38007], [-80.7396, 35.38007], [-80.73754, 35.37989], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.72937, 35.38327], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73029, 35.39311]]}, "date": "2012-02-26", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72865, 35.40114], [-80.73028, 35.40139], [-80.73028, 35.40139], [-80.73013, 35.40186], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75277, 35.42072], [-80.75314, 35.42107], [-80.75352, 35.42159], [-80.75726, 35.42794], [-80.75893, 35.42995], [-80.75928, 35.43016], [-80.75977, 35.43028], [-80.75977, 35.43028], [-80.76035, 35.43043], [-80.7609, 35.43069], [-80.7615, 35.43114], [-80.76222, 35.43187], [-80.76251, 35.43225], [-80.76261, 35.43246], [-80.76266, 35.43278], [-80.76265, 35.43332], [-80.76279, 35.43377], [-80.76308, 35.43413], [-80.76379, 35.43464], [-80.76412, 35.43503], [-80.76434, 35.43565], [-80.76441, 35.4362], [-80.76453, 35.43788], [-80.76452, 35.4382], [-80.76442, 35.43866], [-80.76442, 35.43866], [-80.76392, 35.43969], [-80.76382, 35.4401], [-80.76386, 35.44109], [-80.76401, 35.44217], [-80.76426, 35.44283], [-80.7658, 35.4448], [-80.76597, 35.44519], [-80.76602, 35.44555], [-80.76596, 35.44596], [-80.76568, 35.44642], [-80.76487, 35.44716], [-80.76418, 35.44801], [-80.76401, 35.44829], [-80.76397, 35.44856], [-80.76409, 35.44928], [-80.76409, 35.44955], [-80.76402, 35.45106], [-80.76393, 35.4515], [-80.76371, 35.45188], [-80.76308, 35.45271], [-80.76264, 35.45402], [-80.76246, 35.45445], [-80.76209, 35.4551], [-80.76173, 35.45558], [-80.76115, 35.45618], [-80.75998, 35.45718], [-80.75959, 35.45762], [-80.75939, 35.45793], [-80.75932, 35.45819], [-80.75922, 35.45922], [-80.75855, 35.46121], [-80.75779, 35.46275], [-80.75738, 35.46478], [-80.75633, 35.46824], [-80.75619, 35.46857], [-80.75016, 35.47668], [-80.75016, 35.47668], [-80.75134, 35.47748], [-80.75278, 35.47857], [-80.75522, 35.48055], [-80.75605, 35.48133], [-80.75679, 35.48229], [-80.75731, 35.48316], [-80.76001, 35.48856], [-80.76001, 35.48856], [-80.76517, 35.49041], [-80.76572, 35.49067], [-80.7662, 35.491], [-80.76651, 35.49136], [-80.76922, 35.49559], [-80.7696, 35.49637], [-80.76985, 35.49736], [-80.76989, 35.49793], [-80.76983, 35.4986], [-80.76828, 35.50577], [-80.7682, 35.50647], [-80.76821, 35.50732], [-80.76828, 35.50795], [-80.76886, 35.51102], [-80.76886, 35.51102], [-80.76772, 35.51127], [-80.76706, 35.51148], [-80.76655, 35.51178], [-80.76581, 35.51235], [-80.76537, 35.51287], [-80.76374, 35.51563], [-80.76332, 35.5163], [-80.76309, 35.51657], [-80.76243, 35.51704], [-80.76, 35.51842], [-80.75942, 35.5188], [-80.75731, 35.52101], [-80.75303, 35.52485], [-80.75246, 35.52527], [-80.75188, 35.52549], [-80.75144, 35.52552], [-80.7511, 35.52549], [-80.74819, 35.52429], [-80.74765, 35.52408], [-80.74724, 35.52398], [-80.74691, 35.52395], [-80.74635, 35.52405], [-80.74587, 35.52426], [-80.74558, 35.52457], [-80.74537, 35.52509], [-80.74502, 35.52725], [-80.74483, 35.52763], [-80.74371, 35.52938], [-80.74354, 35.52976], [-80.74345, 35.53017], [-80.74341, 35.53081], [-80.74345, 35.53743], [-80.74379, 35.54489], [-80.74379, 35.54489], [-80.74291, 35.54435], [-80.7424, 35.54414], [-80.74155, 35.54406], [-80.73993, 35.54414], [-80.73993, 35.54414], [-80.7388, 35.54418], [-80.73405, 35.54383], [-80.72857, 35.54335], [-80.72499, 35.54335], [-80.72427, 35.5434], [-80.72177, 35.5437], [-80.72013, 35.54382], [-80.71714, 35.54435], [-80.71653, 35.54442], [-80.71303, 35.54451], [-80.71017, 35.54436], [-80.70919, 35.54426], [-80.70796, 35.54448], [-80.70689, 35.54477], [-80.70623, 35.545], [-80.70409, 35.54627], [-80.70184, 35.54724], [-80.7006, 35.54786], [-80.69996, 35.54823], [-80.69965, 35.54846], [-80.69885, 35.54916], [-80.69842, 35.54969], [-80.69796, 35.55039], [-80.69677, 35.55165], [-80.69406, 35.55412], [-80.69357, 35.55478], [-80.6932, 35.55545], [-80.69223, 35.55785], [-80.69191, 35.55831], [-80.69154, 35.55874], [-80.69083, 35.55942], [-80.68902, 35.56071], [-80.68776, 35.56187], [-80.68718, 35.56256], [-80.68623, 35.56421], [-80.68359, 35.56943], [-80.68304, 35.57015], [-80.68093, 35.57231], [-80.68076, 35.57254], [-80.68076, 35.57254], [-80.68087, 35.57421], [-80.68073, 35.57475], [-80.67895, 35.579], [-80.67848, 35.58002], [-80.67784, 35.58115], [-80.67634, 35.58406], [-80.67584, 35.58454], [-80.67584, 35.58454], [-80.67506, 35.58511], [-80.67396, 35.5854], [-80.67304, 35.58547], [-80.6698, 35.58543], [-80.66837, 35.58567], [-80.66709, 35.58567], [-80.66534, 35.5863], [-80.66507, 35.58646], [-80.6647, 35.58682], [-80.66437, 35.58724], [-80.66417, 35.58739], [-80.66371, 35.58757], [-80.66105, 35.58775], [-80.66035, 35.58774], [-80.65812, 35.5873], [-80.65641, 35.58718], [-80.65565, 35.58718], [-80.6527, 35.58763], [-80.6527, 35.58763], [-80.65273, 35.58857], [-80.65327, 35.59368], [-80.65327, 35.59368], [-80.65085, 35.59535], [-80.64736, 35.59687], [-80.6467, 35.59722], [-80.64532, 35.59825], [-80.64409, 35.59907], [-80.64194, 35.60022], [-80.64032, 35.60118], [-80.63993, 35.60169], [-80.63895, 35.60322], [-80.6374, 35.60549], [-80.63682, 35.60622], [-80.63527, 35.60775], [-80.63342, 35.60937], [-80.63298, 35.60982], [-80.63289, 35.6105], [-80.63283, 35.61211], [-80.63277, 35.61693], [-80.63263, 35.61728], [-80.63238, 35.61758], [-80.63199, 35.61793], [-80.63073, 35.61892], [-80.63008, 35.61939], [-80.6288, 35.62012], [-80.62801, 35.62064], [-80.62765, 35.62104], [-80.62675, 35.62223], [-80.62608, 35.62287], [-80.62499, 35.6237], [-80.62172, 35.62569], [-80.62117, 35.62622], [-80.61912, 35.62911], [-80.61857, 35.62961], [-80.61717, 35.63074], [-80.61152, 35.63602], [-80.60835, 35.63937], [-80.60807, 35.63975], [-80.60748, 35.6408], [-80.60736, 35.64093], [-80.60643, 35.64154], [-80.60591, 35.64176], [-80.60497, 35.64185], [-80.60208, 35.64188], [-80.60147, 35.64192], [-80.60108, 35.64205], [-80.60091, 35.64228], [-80.6007, 35.64478], [-80.60045, 35.64891], [-80.6001, 35.65029], [-80.6001, 35.65029], [-80.59926, 35.65043], [-80.59803, 35.65076], [-80.59543, 35.65163], [-80.59442, 35.65199], [-80.59171, 35.65326], [-80.59088, 35.65358], [-80.59042, 35.6537], [-80.58962, 35.65388], [-80.58882, 35.65394], [-80.58789, 35.65392], [-80.58593, 35.6537], [-80.58593, 35.6537], [-80.58592, 35.65575], [-80.58566, 35.66328], [-80.58551, 35.66404], [-80.58425, 35.66831], [-80.58414, 35.6689], [-80.58398, 35.67186], [-80.58326, 35.67603], [-80.5831, 35.67638], [-80.58165, 35.67799], [-80.58143, 35.67846], [-80.58138, 35.67879], [-80.58132, 35.68435], [-80.58124, 35.68512], [-80.58086, 35.68621], [-80.58086, 35.68621], [-80.57986, 35.68637], [-80.56704, 35.68778], [-80.56655, 35.6878], [-80.56562, 35.68777], [-80.56426, 35.68753], [-80.56333, 35.68719], [-80.56186, 35.68652], [-80.56186, 35.68652], [-80.56176, 35.68904], [-80.56189, 35.69098], [-80.56209, 35.69245], [-80.56255, 35.69495], [-80.56258, 35.69578], [-80.56243, 35.69633], [-80.56064, 35.6988], [-80.55941, 35.70033], [-80.55719, 35.70326], [-80.55543, 35.7058], [-80.55543, 35.7058], [-80.55443, 35.70536], [-80.55384, 35.7052], [-80.55326, 35.7051], [-80.54783, 35.70477], [-80.54382, 35.70426], [-80.5432, 35.70414], [-80.5422, 35.70375], [-80.53373, 35.69957], [-80.52648, 35.69693], [-80.52563, 35.69668], [-80.51282, 35.69379], [-80.51282, 35.69379], [-80.51281, 35.6935], [-80.51269, 35.69305], [-80.51204, 35.69158], [-80.51178, 35.69122], [-80.51143, 35.69049], [-80.50955, 35.68748], [-80.50849, 35.68569], [-80.50785, 35.68339], [-80.50758, 35.68281], [-80.50707, 35.68198], [-80.50496, 35.6782], [-80.50412, 35.67661], [-80.50339, 35.67551], [-80.50256, 35.6745], [-80.50095, 35.67278], [-80.50036, 35.67206], [-80.49993, 35.67135], [-80.49967, 35.67064], [-80.49958, 35.67023], [-80.49944, 35.66879], [-80.49952, 35.66867], [-80.4995, 35.66849], [-80.4995, 35.66849], [-80.4988, 35.66855], [-80.49823, 35.66868], [-80.4975, 35.66896], [-80.49698, 35.66931], [-80.49603, 35.6702], [-80.49503, 35.67127], [-80.49448, 35.67209], [-80.49268, 35.67442], [-80.49231, 35.67479], [-80.4905, 35.67573], [-80.48961, 35.67659], [-80.48855, 35.67862], [-80.48746, 35.68043], [-80.48484, 35.68686], [-80.48484, 35.68686], [-80.49025, 35.68859], [-80.49209, 35.68914], [-80.5007, 35.69104], [-80.50785, 35.69271], [-80.50955, 35.69307], [-80.51032, 35.69328], [-80.51048, 35.69336], [-80.5128, 35.6939], [-80.5128, 35.6939], [-80.51281, 35.6935], [-80.51269, 35.69305], [-80.51204, 35.69158], [-80.51178, 35.69122]]}, "date": "2012-03-06", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72939, 35.40268], [-80.72923, 35.40296], [-80.72912, 35.40332], [-80.72908, 35.40393], [-80.72956, 35.40612], [-80.72976, 35.40661], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75256, 35.42057], [-80.75294, 35.42086], [-80.7534, 35.42141], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.75628, 35.42376], [-80.75713, 35.42358], [-80.7578, 35.42352], [-80.75811, 35.42354], [-80.75839, 35.42362], [-80.75971, 35.42409], [-80.76016, 35.42421], [-80.7631, 35.42467], [-80.764, 35.42478], [-80.7644, 35.42474], [-80.76462, 35.42464], [-80.76747, 35.4226], [-80.76792, 35.42236], [-80.77201, 35.42084], [-80.77282, 35.42064], [-80.77335, 35.42061], [-80.77484, 35.42075], [-80.77551, 35.42084], [-80.77589, 35.42095], [-80.77666, 35.42129], [-80.77713, 35.42156], [-80.77754, 35.4219], [-80.77798, 35.4225], [-80.77821, 35.42269], [-80.77896, 35.42292], [-80.78116, 35.42347], [-80.78176, 35.42352], [-80.78718, 35.4232], [-80.7875, 35.42319], [-80.7879, 35.42324], [-80.78822, 35.42335], [-80.79105, 35.42488], [-80.79201, 35.42529], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79428, 35.42758], [-80.7996, 35.43312], [-80.80167, 35.43512], [-80.80274, 35.43638], [-80.80293, 35.43653], [-80.80293, 35.43653], [-80.80302, 35.43527], [-80.80308, 35.43501], [-80.80344, 35.4342], [-80.80366, 35.43384], [-80.8078, 35.43011], [-80.80848, 35.42956], [-80.80948, 35.42904], [-80.81276, 35.42752], [-80.81897, 35.42473], [-80.8194, 35.42462], [-80.8224, 35.42423], [-80.8224, 35.42423], [-80.82255, 35.4248], [-80.82316, 35.42607], [-80.82316, 35.42607], [-80.82347, 35.426], [-80.82432, 35.42569], [-80.82507, 35.42566], [-80.82623, 35.4258], [-80.82701, 35.42598], [-80.82788, 35.42627], [-80.82826, 35.42628], [-80.82887, 35.42618], [-80.83203, 35.42517], [-80.83248, 35.42507], [-80.83313, 35.42506], [-80.83416, 35.42532], [-80.83416, 35.42532], [-80.8343, 35.42468], [-80.83443, 35.42437], [-80.83469, 35.42417], [-80.83518, 35.42393], [-80.83518, 35.42393], [-80.8348, 35.42346], [-80.83459, 35.42327], [-80.8339, 35.42282], [-80.83336, 35.42258], [-80.83267, 35.4224], [-80.83178, 35.42224], [-80.83159, 35.42209], [-80.83159, 35.42209], [-80.83599, 35.42066], [-80.83636, 35.42058], [-80.83667, 35.42058], [-80.83772, 35.4208], [-80.83801, 35.42082], [-80.84202, 35.42075], [-80.84202, 35.42075], [-80.8419, 35.41953], [-80.84201, 35.41826], [-80.84233, 35.4171], [-80.84296, 35.41521], [-80.84318, 35.41427], [-80.8432, 35.41376], [-80.84295, 35.4108], [-80.84295, 35.4108], [-80.85189, 35.41046], [-80.85267, 35.4104], [-80.85322, 35.41031], [-80.8572, 35.4092], [-80.85859, 35.40875], [-80.85883, 35.40872], [-80.85967, 35.40846], [-80.8606, 35.40814], [-80.86629, 35.40594], [-80.86938, 35.4047], [-80.86999, 35.40456], [-80.87067, 35.40448], [-80.87121, 35.40458], [-80.87183, 35.40484], [-80.87328, 35.4058], [-80.87366, 35.406], [-80.87411, 35.40616], [-80.87472, 35.40627], [-80.8753, 35.40622], [-80.87557, 35.40615], [-80.8762, 35.40589], [-80.87757, 35.40517], [-80.87892, 35.40427], [-80.87929, 35.4041], [-80.87964, 35.40407], [-80.88126, 35.40422], [-80.88181, 35.40436], [-80.88487, 35.40601], [-80.88585, 35.40666], [-80.88612, 35.40678], [-80.88648, 35.40687], [-80.88695, 35.4069], [-80.88826, 35.40686], [-80.89185, 35.40708], [-80.89266, 35.40709], [-80.89463, 35.40674], [-80.89694, 35.40652], [-80.89748, 35.40657], [-80.89946, 35.40688], [-80.90111, 35.4074], [-80.90111, 35.4074], [-80.90163, 35.40752], [-80.90222, 35.40755], [-80.90416, 35.40744], [-80.90457, 35.40737], [-80.90495, 35.40724], [-80.90528, 35.40705], [-80.90553, 35.4068], [-80.90608, 35.40598], [-80.90695, 35.40527], [-80.90724, 35.4051], [-80.90758, 35.40496], [-80.90793, 35.40491], [-80.90896, 35.40485], [-80.90967, 35.40489], [-80.91062, 35.40486], [-80.91128, 35.40475], [-80.91156, 35.40462], [-80.91179, 35.40443], [-80.91328, 35.40254], [-80.91363, 35.40219], [-80.9138, 35.4021], [-80.91415, 35.40201], [-80.91651, 35.40181], [-80.91651, 35.40181], [-80.9164, 35.40095], [-80.9164, 35.40095], [-80.91659, 35.40092], [-80.91672, 35.40084], [-80.91678, 35.40074], [-80.91672, 35.39991], [-80.91664, 35.39969], [-80.9164, 35.39931], [-80.91581, 35.39789], [-80.91581, 35.39789], [-80.91674, 35.39767], [-80.91906, 35.39753], [-80.91934, 35.39748], [-80.91958, 35.39737], [-80.9199, 35.3971], [-80.92047, 35.39649], [-80.92067, 35.39615], [-80.92072, 35.3957], [-80.92051, 35.39513], [-80.92051, 35.39513], [-80.92166, 35.39484], [-80.92184, 35.39483], [-80.92205, 35.39484], [-80.92291, 35.39509], [-80.9233, 35.39514], [-80.9233, 35.39514], [-80.92332, 35.39488], [-80.92325, 35.39456], [-80.92238, 35.39295], [-80.92238, 35.39295], [-80.92289, 35.39277], [-80.92289, 35.39277], [-80.92258, 35.39226], [-80.92129, 35.39095], [-80.921, 35.39048], [-80.9209, 35.39002], [-80.92104, 35.38889], [-80.92101, 35.38856], [-80.9204, 35.38572], [-80.91992, 35.38444], [-80.91896, 35.38215], [-80.91855, 35.38032], [-80.91836, 35.37999], [-80.91784, 35.37962], [-80.91459, 35.37792], [-80.91419, 35.3778], [-80.90934, 35.37673], [-80.90893, 35.37659], [-80.90856, 35.37638], [-80.90685, 35.37518], [-80.90685, 35.37518], [-80.90142, 35.38045], [-80.90098, 35.38081], [-80.9007, 35.38095], [-80.9003, 35.38106], [-80.89887, 35.38119], [-80.8984, 35.38128], [-80.89811, 35.3814], [-80.89388, 35.38436], [-80.89335, 35.38469], [-80.89222, 35.3852], [-80.89078, 35.38558], [-80.88752, 35.38629], [-80.88706, 35.38638], [-80.88671, 35.3864], [-80.88603, 35.38633], [-80.88532, 35.38612], [-80.87815, 35.38234], [-80.8773, 35.38191], [-80.87692, 35.38177], [-80.87614, 35.38159], [-80.86708, 35.38], [-80.86632, 35.37982], [-80.86536, 35.3795], [-80.85869, 35.37686], [-80.85415, 35.37558], [-80.83385, 35.37753], [-80.8336, 35.37748], [-80.8336, 35.37748], [-80.83331, 35.37468], [-80.83302, 35.37329], [-80.83263, 35.37206], [-80.83241, 35.37154], [-80.83199, 35.37084], [-80.83161, 35.37037], [-80.83152, 35.37014], [-80.83152, 35.37014], [-80.8312, 35.37016], [-80.82979, 35.37005], [-80.82774, 35.36978], [-80.8263, 35.36936], [-80.8223, 35.36804], [-80.82146, 35.36753], [-80.82113, 35.36721], [-80.81993, 35.36484], [-80.81967, 35.36392], [-80.81967, 35.36392], [-80.81933, 35.36421], [-80.81818, 35.365], [-80.81581, 35.3672], [-80.81521, 35.3677], [-80.81482, 35.36795], [-80.80745, 35.37203], [-80.80745, 35.37203], [-80.80692, 35.37136], [-80.80675, 35.3709], [-80.80678, 35.37028], [-80.807, 35.36904], [-80.80697, 35.3687], [-80.80684, 35.3683], [-80.80602, 35.36752], [-80.80511, 35.36677], [-80.80399, 35.36514], [-80.8035, 35.36483], [-80.80261, 35.36458], [-80.80218, 35.36438], [-80.80105, 35.36326], [-80.80062, 35.36265], [-80.80048, 35.36233], [-80.79995, 35.36032], [-80.79995, 35.36032], [-80.79743, 35.36106], [-80.79256, 35.36327], [-80.78935, 35.3644], [-80.7887, 35.3647], [-80.78749, 35.36537], [-80.78749, 35.36537], [-80.78687, 35.36561], [-80.78602, 35.36574], [-80.78445, 35.36564], [-80.78445, 35.36564], [-80.78435, 35.36537], [-80.78399, 35.36477], [-80.78391, 35.36451], [-80.78392, 35.36417], [-80.78396, 35.36396], [-80.78425, 35.36346], [-80.78474, 35.36296], [-80.78474, 35.36296], [-80.78541, 35.36326], [-80.78712, 35.36482], [-80.78729, 35.365], [-80.78747, 35.3653], [-80.78754, 35.36569], [-80.7875, 35.36598], [-80.7873, 35.36639], [-80.78683, 35.36795], [-80.78572, 35.36951], [-80.78508, 35.3705], [-80.78471, 35.3712], [-80.78457, 35.37191], [-80.78472, 35.37284], [-80.78472, 35.37284], [-80.78339, 35.37296], [-80.78295, 35.3729], [-80.7817, 35.37251], [-80.78139, 35.37247], [-80.78075, 35.37251], [-80.77909, 35.37297], [-80.77405, 35.37449], [-80.77076, 35.37531], [-80.77037, 35.37537], [-80.77002, 35.37536], [-80.76961, 35.37529], [-80.76517, 35.37399], [-80.76413, 35.37375], [-80.76381, 35.3737], [-80.76241, 35.37365], [-80.76186, 35.37358], [-80.76125, 35.37338], [-80.76079, 35.37315], [-80.7582, 35.37169], [-80.75623, 35.371], [-80.75575, 35.37073], [-80.75496, 35.37006], [-80.75376, 35.36863], [-80.75176, 35.36689], [-80.74977, 35.36557], [-80.74935, 35.36524], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74841, 35.36441], [-80.74807, 35.36734], [-80.74768, 35.36903], [-80.74752, 35.36956], [-80.74734, 35.36994], [-80.74705, 35.37032], [-80.74668, 35.37067], [-80.74567, 35.37137], [-80.74519, 35.37161], [-80.7433, 35.3722], [-80.74276, 35.37251], [-80.74218, 35.37306], [-80.74182, 35.37368], [-80.7397, 35.37996], [-80.7396, 35.38007], [-80.7396, 35.38007], [-80.73754, 35.37989], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.7294, 35.38313], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73034, 35.39461], [-80.73051, 35.39643], [-80.7309, 35.39864], [-80.73028, 35.40139]]}, "date": "2012-03-08", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-78.86511, 35.77245], [-78.86528, 35.77267], [-78.86534, 35.77284], [-78.86515, 35.77427], [-78.86518, 35.77466], [-78.86518, 35.77466], [-78.8667, 35.77463], [-78.86701, 35.77456], [-78.86747, 35.77437], [-78.86777, 35.77413], [-78.86797, 35.77388], [-78.86821, 35.77329], [-78.86821, 35.77329], [-78.86894, 35.77339], [-78.87037, 35.77333], [-78.87169, 35.77347], [-78.87191, 35.77345], [-78.87258, 35.77308], [-78.87296, 35.77305], [-78.87339, 35.7731], [-78.87339, 35.7731], [-78.8757, 35.76102], [-78.87603, 35.75903], [-78.87681, 35.75501], [-78.87704, 35.75354], [-78.87712, 35.75262], [-78.87711, 35.75149], [-78.87673, 35.75016], [-78.87633, 35.74931], [-78.87633, 35.74931], [-78.87563, 35.7496], [-78.87517, 35.7497], [-78.87502, 35.74969], [-78.87477, 35.74961], [-78.87449, 35.74937], [-78.87442, 35.74912], [-78.87446, 35.74887], [-78.87472, 35.74863], [-78.87493, 35.74854], [-78.87579, 35.74839], [-78.8821, 35.74792], [-78.89482, 35.74685], [-78.90031, 35.74631], [-78.90471, 35.74582], [-78.95019, 35.74127], [-78.95263, 35.74098], [-78.97415, 35.73745], [-78.98747, 35.73557], [-78.98834, 35.73548], [-78.98926, 35.73547], [-78.98991, 35.73551], [-79.0003, 35.73684], [-79.00345, 35.73714], [-79.00631, 35.73752], [-79.00767, 35.73764], [-79.00879, 35.73768], [-79.01002, 35.73766], [-79.01698, 35.73717], [-79.01876, 35.73714], [-79.05511, 35.73871], [-79.06538, 35.73958], [-79.066, 35.73958], [-79.06808, 35.73943], [-79.06906, 35.73926], [-79.07038, 35.73896], [-79.07187, 35.73853], [-79.07603, 35.73747], [-79.07783, 35.73727], [-79.08366, 35.73696], [-79.08758, 35.73661], [-79.09328, 35.73588], [-79.09518, 35.73554], [-79.0991, 35.73465], [-79.10132, 35.7339], [-79.10334, 35.7331], [-79.10729, 35.73137], [-79.11103, 35.72965], [-79.11392, 35.72838], [-79.12029, 35.72567], [-79.12029, 35.72567], [-79.12113, 35.72537], [-79.12205, 35.7252], [-79.1232, 35.72517], [-79.12514, 35.72538], [-79.12593, 35.72535], [-79.12676, 35.72509], [-79.12711, 35.72496], [-79.12787, 35.72451], [-79.12856, 35.72386], [-79.12983, 35.72237], [-79.12983, 35.72237], [-79.13064, 35.72155], [-79.13068, 35.72146], [-79.13091, 35.72128], [-79.13205, 35.72065], [-79.13306, 35.72024], [-79.14427, 35.71821], [-79.14531, 35.71809], [-79.14612, 35.71811], [-79.1473, 35.71823], [-79.1534, 35.71917], [-79.15475, 35.71934], [-79.15645, 35.71949], [-79.15895, 35.71962], [-79.17642, 35.72028], [-79.17684, 35.72039], [-79.17684, 35.72039], [-79.17693, 35.72051], [-79.17706, 35.72057], [-79.17728, 35.72056], [-79.17746, 35.72042], [-79.17801, 35.72034], [-79.18516, 35.72058], [-79.18686, 35.72066], [-79.1874, 35.72072], [-79.23267, 35.72712], [-79.23267, 35.72712], [-79.23303, 35.72719], [-79.23366, 35.72741], [-79.23515, 35.72817], [-79.23557, 35.72833], [-79.23593, 35.72845], [-79.23665, 35.72858], [-79.23744, 35.72857], [-79.23844, 35.72842], [-79.23889, 35.72831], [-79.23978, 35.72821], [-79.24027, 35.72821], [-79.2413, 35.72831], [-79.27388, 35.73284], [-79.27644, 35.73306], [-79.27809, 35.73314], [-79.28184, 35.73318], [-79.28924, 35.73338], [-79.29509, 35.73345], [-79.29829, 35.73354], [-79.32513, 35.73404], [-79.33289, 35.73423], [-79.34194, 35.73435], [-79.34465, 35.73423], [-79.35053, 35.73389], [-79.35297, 35.73371], [-79.3579, 35.73347], [-79.36653, 35.73297], [-79.36868, 35.73293], [-79.37057, 35.73298], [-79.38711, 35.73374], [-79.40905, 35.73481], [-79.40946, 35.73477], [-79.41011, 35.7348], [-79.41342, 35.73496], [-79.41394, 35.73503], [-79.41668, 35.73522], [-79.41901, 35.73531], [-79.42665, 35.73473], [-79.4269, 35.73478], [-79.42946, 35.73457], [-79.43131, 35.73439], [-79.4332, 35.73415], [-79.43397, 35.73412], [-79.43429, 35.73408], [-79.43453, 35.734], [-79.43805, 35.73367], [-79.44529, 35.73286], [-79.44766, 35.73276], [-79.47644, 35.73377], [-79.4774, 35.73376], [-79.48374, 35.73337], [-79.48527, 35.73335], [-79.49954, 35.73404], [-79.50458, 35.73435], [-79.50498, 35.73442], [-79.50714, 35.73461], [-79.51357, 35.735], [-79.51834, 35.73518], [-79.51925, 35.7353], [-79.52118, 35.7358], [-79.54688, 35.74298], [-79.54778, 35.74321], [-79.54915, 35.74346], [-79.55019, 35.74355], [-79.55157, 35.74353], [-79.55627, 35.74273], [-79.55819, 35.74265], [-79.57042, 35.7424], [-79.57232, 35.74257], [-79.57896, 35.74391], [-79.57896, 35.74391], [-79.58023, 35.74218], [-79.58023, 35.74218], [-79.58329, 35.74267], [-79.58433, 35.7428], [-79.58494, 35.7428], [-79.58681, 35.74225], [-79.59082, 35.74091], [-79.59192, 35.74075], [-79.59324, 35.7407], [-79.59421, 35.74073], [-79.596, 35.74147], [-79.59643, 35.7416], [-79.60168, 35.74193], [-79.60251, 35.7419], [-79.60302, 35.74179], [-79.60367, 35.74159], [-79.60424, 35.74135], [-79.60878, 35.73855], [-79.61068, 35.73758], [-79.61157, 35.73705], [-79.6128, 35.73578], [-79.61303, 35.73533], [-79.61331, 35.73457], [-79.61358, 35.73419], [-79.61427, 35.7337], [-79.61559, 35.733], [-79.61616, 35.73277], [-79.61616, 35.73277], [-79.61612, 35.73129], [-79.61612, 35.73129], [-79.62126, 35.73144], [-79.62243, 35.73146], [-79.62281, 35.73143], [-79.62388, 35.73128], [-79.62993, 35.72999], [-79.63091, 35.7299], [-79.63238, 35.72997], [-79.63404, 35.73023], [-79.63719, 35.7303], [-79.63762, 35.73034], [-79.64002, 35.73084], [-79.64307, 35.7308], [-79.645, 35.73095], [-79.64577, 35.73096], [-79.64618, 35.73093], [-79.64684, 35.73075], [-79.64684, 35.73075], [-79.64713, 35.73107], [-79.64813, 35.73173], [-79.6489, 35.73243], [-79.64951, 35.73277], [-79.65001, 35.73297], [-79.651, 35.73327], [-79.65215, 35.73319], [-79.65215, 35.73319], [-79.6527, 35.73267], [-79.65346, 35.73211], [-79.65377, 35.73195], [-79.65415, 35.73191], [-79.65415, 35.73191], [-79.65412, 35.73149], [-79.65412, 35.73149], [-79.65891, 35.72947], [-79.6592, 35.72918], [-79.65971, 35.72835], [-79.66067, 35.72711], [-79.66099, 35.72651], [-79.66099, 35.72651], [-79.66168, 35.72643], [-79.66268, 35.7264], [-79.66268, 35.7264], [-79.66338, 35.72639], [-79.6654, 35.72666], [-79.66723, 35.72721], [-79.6682, 35.72738], [-79.67197, 35.72768], [-79.67454, 35.7276], [-79.67562, 35.7277], [-79.67674, 35.7279], [-79.67942, 35.72865], [-79.68015, 35.72874], [-79.68062, 35.72872], [-79.68109, 35.72864], [-79.68167, 35.72844], [-79.68229, 35.7281], [-79.68369, 35.72702], [-79.68369, 35.72702], [-79.68456, 35.7286], [-79.68456, 35.7286], [-79.68579, 35.72826], [-79.68696, 35.728], [-79.68936, 35.72754], [-79.69505, 35.72658], [-79.69736, 35.72626], [-79.70305, 35.72569], [-79.70503, 35.7254], [-79.71362, 35.72433], [-79.71413, 35.7242], [-79.71572, 35.72419], [-79.72794, 35.72452], [-79.73046, 35.72463], [-79.73616, 35.72504], [-79.74024, 35.72528], [-79.74127, 35.72518], [-79.74546, 35.72455], [-79.7478, 35.72432], [-79.74829, 35.72433], [-79.75074, 35.72387], [-79.75162, 35.72361], [-79.7535, 35.72278], [-79.76014, 35.71875], [-79.76382, 35.71659], [-79.7639, 35.71645], [-79.77226, 35.71149], [-79.77285, 35.7111], [-79.77389, 35.71023], [-79.77549, 35.7086], [-79.77613, 35.70805], [-79.7802, 35.70486], [-79.78738, 35.69843], [-79.78772, 35.69808], [-79.78903, 35.69708], [-79.78981, 35.69657], [-79.79089, 35.69599], [-79.79206, 35.69547], [-79.79316, 35.69501], [-79.79326, 35.69502], [-79.80184, 35.69136], [-79.80534, 35.68999], [-79.80619, 35.68971], [-79.80855, 35.68912], [-79.81028, 35.68877], [-79.8127, 35.6885], [-79.82243, 35.68783], [-79.82396, 35.68763], [-79.82579, 35.68726], [-79.82681, 35.68693], [-79.82695, 35.68695], [-79.82828, 35.68649], [-79.83156, 35.68552], [-79.83272, 35.68536], [-79.8339, 35.68541], [-79.83455, 35.68553], [-79.83547, 35.68572], [-79.83839, 35.68646], [-79.83839, 35.68646], [-79.83756, 35.68511], [-79.83756, 35.68511], [-79.83938, 35.68449], [-79.84262, 35.68326], [-79.84802, 35.68135], [-79.84888, 35.68109], [-79.85096, 35.68027], [-79.85879, 35.67589], [-79.86197, 35.67435], [-79.86338, 35.67387], [-79.86477, 35.6735], [-79.86853, 35.67284], [-79.87019, 35.67247], [-79.87162, 35.67206], [-79.87273, 35.67165], [-79.88061, 35.66816], [-79.88259, 35.66741], [-79.88379, 35.66703], [-79.88912, 35.66593], [-79.89154, 35.66536], [-79.89275, 35.66496], [-79.91029, 35.65787], [-79.91856, 35.65488], [-79.92066, 35.65426], [-79.92266, 35.65387], [-79.92868, 35.65334], [-79.93073, 35.65301], [-79.93278, 35.65249], [-79.93622, 35.65115], [-79.94905, 35.646], [-79.95054, 35.64553], [-79.95211, 35.64512], [-79.95443, 35.6448], [-79.95897, 35.6443], [-79.96144, 35.64378], [-79.96281, 35.64331], [-79.96398, 35.64277], [-79.96698, 35.64108], [-79.96827, 35.64047], [-79.96984, 35.63995], [-79.97053, 35.63978], [-79.97241, 35.63946], [-79.97301, 35.6394], [-79.97405, 35.63939], [-79.97977, 35.63952], [-79.98106, 35.63946], [-79.99456, 35.63743], [-79.99559, 35.63724], [-79.99653, 35.63703], [-79.9971, 35.63683], [-79.99764, 35.63662], [-79.99881, 35.63605], [-79.99972, 35.63543], [-80.00057, 35.63467], [-80.00151, 35.6337], [-80.00561, 35.62604], [-80.00601, 35.62533], [-80.00657, 35.62456], [-80.0075, 35.6235], [-80.00828, 35.6227], [-80.01073, 35.61987], [-80.01768, 35.61204], [-80.01886, 35.61084], [-80.02012, 35.60973], [-80.02467, 35.60616], [-80.03078, 35.6015], [-80.03209, 35.60057], [-80.03358, 35.59967], [-80.03634, 35.5983], [-80.03713, 35.59788], [-80.03793, 35.59738], [-80.03913, 35.59655], [-80.04467, 35.59241], [-80.04671, 35.59111], [-80.05364, 35.58694], [-80.05655, 35.58508], [-80.05964, 35.58288], [-80.06417, 35.57974], [-80.0678, 35.57692], [-80.08174, 35.56575], [-80.08453, 35.56364], [-80.09, 35.55932], [-80.09076, 35.55864], [-80.10252, 35.54928], [-80.10319, 35.54865], [-80.10333, 35.54842], [-80.10483, 35.54682], [-80.10562, 35.54612], [-80.1061, 35.5458], [-80.10659, 35.54555], [-80.11281, 35.54354], [-80.11399, 35.54307], [-80.11468, 35.54264], [-80.11797, 35.53954], [-80.11866, 35.53902], [-80.11866, 35.53892], [-80.1212, 35.5365], [-80.12464, 35.53334], [-80.13174, 35.52648], [-80.1326, 35.52561], [-80.13367, 35.52437], [-80.13468, 35.52306], [-80.13654, 35.52081], [-80.13736, 35.51996], [-80.13858, 35.51898], [-80.14065, 35.51748], [-80.14457, 35.51474], [-80.14641, 35.51335], [-80.14761, 35.51273], [-80.14784, 35.51264], [-80.14869, 35.51245], [-80.14974, 35.51235], [-80.16021, 35.5122], [-80.17045, 35.51021], [-80.17461, 35.5093], [-80.17688, 35.50885], [-80.17707, 35.50889], [-80.18007, 35.50832], [-80.18084, 35.50812], [-80.18167, 35.50769], [-80.18631, 35.50445], [-80.18716, 35.50401], [-80.18871, 35.50354], [-80.19137, 35.50288], [-80.19153, 35.50278], [-80.19747, 35.50136], [-80.19828, 35.50108], [-80.19943, 35.50054], [-80.20036, 35.49997], [-80.2012, 35.49931], [-80.20546, 35.49522], [-80.20633, 35.49446], [-80.20757, 35.4937], [-80.20819, 35.4934], [-80.22635, 35.48599], [-80.2361, 35.48206], [-80.23853, 35.48123], [-80.23997, 35.4808], [-80.25437, 35.47701], [-80.25614, 35.47648], [-80.25816, 35.47578], [-80.26064, 35.47479], [-80.26827, 35.47113], [-80.26926, 35.47069], [-80.27032, 35.47028], [-80.27186, 35.46987], [-80.27353, 35.46959], [-80.28091, 35.46919], [-80.28423, 35.46905], [-80.28544, 35.46892], [-80.28704, 35.46866], [-80.28781, 35.46849], [-80.28937, 35.46806], [-80.31229, 35.46062], [-80.31376, 35.46022], [-80.31523, 35.45992], [-80.31707, 35.45968], [-80.33566, 35.45752], [-80.33662, 35.45738], [-80.33757, 35.45717], [-80.33842, 35.45696], [-80.33981, 35.45653], [-80.34066, 35.45621], [-80.34194, 35.45565], [-80.34368, 35.45475], [-80.36184, 35.44245], [-80.36316, 35.44167], [-80.36397, 35.44126], [-80.36592, 35.44046], [-80.36735, 35.43997], [-80.3694, 35.43951], [-80.37757, 35.43808], [-80.38182, 35.4374], [-80.38274, 35.43732], [-80.384, 35.43731], [-80.38846, 35.4376], [-80.38985, 35.43757], [-80.39097, 35.43745], [-80.39208, 35.43724], [-80.39338, 35.4369], [-80.39454, 35.43649], [-80.39547, 35.43606], [-80.3962, 35.43568], [-80.41672, 35.42337], [-80.4184, 35.42213], [-80.41954, 35.42109], [-80.42608, 35.41425], [-80.42703, 35.41347], [-80.42805, 35.4128], [-80.42905, 35.41226], [-80.43024, 35.41175], [-80.43196, 35.41122], [-80.43349, 35.41093], [-80.43442, 35.41083], [-80.44029, 35.41059], [-80.44232, 35.41043], [-80.44402, 35.41005], [-80.44537, 35.40961], [-80.47687, 35.39676], [-80.48378, 35.39354], [-80.4881, 35.39163], [-80.48894, 35.3913], [-80.49416, 35.38896], [-80.49841, 35.38695], [-80.49941, 35.38656], [-80.5001, 35.38639], [-80.50087, 35.38629], [-80.5192, 35.38528], [-80.52079, 35.38515], [-80.52223, 35.38486], [-80.5231, 35.38459], [-80.52405, 35.38422], [-80.52499, 35.38377], [-80.52584, 35.38325], [-80.52751, 35.38199], [-80.53259, 35.37783], [-80.53316, 35.37743], [-80.53384, 35.37706], [-80.53498, 35.37663], [-80.53612, 35.37635], [-80.54493, 35.37456], [-80.54604, 35.37437], [-80.54604, 35.37437], [-80.54629, 35.37438], [-80.54744, 35.3742], [-80.54838, 35.37412], [-80.54898, 35.37414], [-80.55071, 35.37446], [-80.55135, 35.37446], [-80.55135, 35.37446], [-80.55151, 35.37539], [-80.5515, 35.37566], [-80.55167, 35.37632], [-80.5519, 35.37688], [-80.55245, 35.37775], [-80.55306, 35.37842], [-80.55367, 35.37898], [-80.55454, 35.37956], [-80.55571, 35.38013], [-80.55667, 35.38045], [-80.55806, 35.3807], [-80.56322, 35.38136], [-80.56542, 35.38151], [-80.5748, 35.38112], [-80.57736, 35.38108], [-80.57815, 35.38112], [-80.57873, 35.3812], [-80.57992, 35.38145], [-80.5809, 35.38177], [-80.58098, 35.38187], [-80.58314, 35.38283], [-80.58411, 35.38332], [-80.58421, 35.38332], [-80.58549, 35.38394], [-80.58612, 35.38417], [-80.58711, 35.3844], [-80.59189, 35.38503], [-80.59249, 35.38517], [-80.59348, 35.38553], [-80.60327, 35.39093], [-80.60439, 35.39166], [-80.60506, 35.39228], [-80.6054, 35.39269], [-80.60582, 35.39336], [-80.60693, 35.39644], [-80.60693, 35.39644], [-80.60739, 35.39632], [-80.61074, 35.39586], [-80.61108, 35.39596], [-80.61123, 35.39609], [-80.61123, 35.39609], [-80.61244, 35.39568], [-80.61371, 35.39544], [-80.61603, 35.39511], [-80.61603, 35.39511], [-80.62518, 35.40051], [-80.62621, 35.40107], [-80.62711, 35.40144], [-80.62711, 35.40144], [-80.6274, 35.40148], [-80.628, 35.40146], [-80.63165, 35.40099], [-80.64086, 35.39871], [-80.64148, 35.39861], [-80.64447, 35.39843], [-80.64577, 35.3984], [-80.64925, 35.39863], [-80.65041, 35.39875], [-80.65101, 35.39878], [-80.65197, 35.39875], [-80.65742, 35.39831], [-80.65943, 35.39804], [-80.66011, 35.3979], [-80.66263, 35.39714], [-80.66346, 35.3968], [-80.66616, 35.39495], [-80.66651, 35.39475], [-80.66732, 35.39439], [-80.66854, 35.39393], [-80.66893, 35.39372], [-80.67052, 35.39243], [-80.67232, 35.39114], [-80.67232, 35.39114], [-80.67352, 35.39245], [-80.6738, 35.39284], [-80.67421, 35.39406], [-80.67429, 35.39459], [-80.67429, 35.39459], [-80.675, 35.39463], [-80.67537, 35.39478], [-80.67561, 35.395], [-80.67596, 35.39588], [-80.67648, 35.39668], [-80.6768, 35.39761], [-80.67698, 35.3979], [-80.67719, 35.39812], [-80.67719, 35.39812], [-80.6767, 35.39851], [-80.67636, 35.39866], [-80.67636, 35.39866], [-80.67662, 35.3993], [-80.67684, 35.39951], [-80.67699, 35.39959], [-80.6773, 35.39965], [-80.67982, 35.39982], [-80.68133, 35.40025], [-80.68164, 35.40043], [-80.68164, 35.40043], [-80.68129, 35.40107], [-80.68131, 35.40175], [-80.68125, 35.40211], [-80.6809, 35.40318], [-80.68092, 35.40359], [-80.68133, 35.40486], [-80.68133, 35.40486], [-80.68486, 35.40475], [-80.68915, 35.40417], [-80.68986, 35.40396], [-80.69314, 35.40241], [-80.69427, 35.40202], [-80.69556, 35.40178], [-80.69617, 35.40173], [-80.69686, 35.40172], [-80.69762, 35.40177], [-80.69845, 35.40189], [-80.69913, 35.40206], [-80.70393, 35.40368], [-80.71314, 35.40736], [-80.71716, 35.40911], [-80.71809, 35.4094], [-80.72187, 35.4098], [-80.72254, 35.40981], [-80.72326, 35.40966], [-80.72802, 35.40826], [-80.72892, 35.40821], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73068, 35.40803], [-80.72976, 35.40661], [-80.72956, 35.40612], [-80.72908, 35.40393], [-80.72912, 35.40332], [-80.72923, 35.40296], [-80.72939, 35.40268]]}, "date": "2012-03-12", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75256, 35.42057], [-80.75294, 35.42086], [-80.7534, 35.42141], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.75628, 35.42376], [-80.75713, 35.42358], [-80.7578, 35.42352], [-80.75811, 35.42354], [-80.75839, 35.42362], [-80.75971, 35.42409], [-80.76016, 35.42421], [-80.7631, 35.42467], [-80.764, 35.42478], [-80.7644, 35.42474], [-80.76462, 35.42464], [-80.76747, 35.4226], [-80.76792, 35.42236], [-80.77201, 35.42084], [-80.77282, 35.42064], [-80.77335, 35.42061], [-80.77484, 35.42075], [-80.77551, 35.42084], [-80.77589, 35.42095], [-80.77666, 35.42129], [-80.77713, 35.42156], [-80.77754, 35.4219], [-80.77798, 35.4225], [-80.77821, 35.42269], [-80.77896, 35.42292], [-80.78116, 35.42347], [-80.78176, 35.42352], [-80.78718, 35.4232], [-80.7875, 35.42319], [-80.7879, 35.42324], [-80.78822, 35.42335], [-80.79105, 35.42488], [-80.79201, 35.42529], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79406, 35.42406], [-80.79483, 35.42324], [-80.79732, 35.42077], [-80.79863, 35.41961], [-80.79885, 35.41926], [-80.80057, 35.41562], [-80.80089, 35.41512], [-80.80186, 35.41438], [-80.8022, 35.41425], [-80.80461, 35.41366], [-80.80461, 35.41366], [-80.80471, 35.41173], [-80.80469, 35.41138], [-80.80462, 35.41114], [-80.80437, 35.41073], [-80.80356, 35.40982], [-80.80334, 35.40933], [-80.8031, 35.40852], [-80.80282, 35.40789], [-80.80251, 35.40739], [-80.80203, 35.40691], [-80.80179, 35.40653], [-80.80133, 35.4049], [-80.80126, 35.40431], [-80.80129, 35.40417], [-80.80147, 35.40387], [-80.80186, 35.40341], [-80.80349, 35.40201], [-80.80366, 35.40169], [-80.80368, 35.40065], [-80.8035, 35.3993], [-80.80238, 35.39644], [-80.80226, 35.39591], [-80.80234, 35.39557], [-80.80272, 35.3948], [-80.80313, 35.39375], [-80.80431, 35.39016], [-80.80459, 35.38905], [-80.80465, 35.38868], [-80.80463, 35.38838], [-80.80426, 35.38685], [-80.8042, 35.38641], [-80.80425, 35.3853], [-80.80437, 35.38469], [-80.80457, 35.3844], [-80.80485, 35.38417], [-80.80551, 35.38377], [-80.80646, 35.38332], [-80.80968, 35.38193], [-80.80989, 35.38181], [-80.80998, 35.38165], [-80.80999, 35.38137], [-80.80989, 35.38108], [-80.80877, 35.37857], [-80.80872, 35.37813], [-80.80881, 35.37619], [-80.80859, 35.37423], [-80.80839, 35.37338], [-80.80821, 35.37295], [-80.8077, 35.37243], [-80.80745, 35.37203], [-80.80745, 35.37203], [-80.80574, 35.37294], [-80.80313, 35.37445], [-80.7944, 35.3804], [-80.79396, 35.38059], [-80.78991, 35.38187], [-80.78948, 35.38206], [-80.78844, 35.38275], [-80.78813, 35.383], [-80.78686, 35.38424], [-80.78668, 35.38429], [-80.78668, 35.38429], [-80.78628, 35.38251], [-80.7859, 35.3803], [-80.78501, 35.37401], [-80.78501, 35.37401], [-80.78493, 35.37402], [-80.78471, 35.37393], [-80.78345, 35.37329], [-80.78327, 35.37313], [-80.78319, 35.37294], [-80.78319, 35.37294], [-80.78269, 35.37283], [-80.7817, 35.37251], [-80.78139, 35.37247], [-80.78075, 35.37251], [-80.77909, 35.37297], [-80.77405, 35.37449], [-80.77076, 35.37531], [-80.77037, 35.37537], [-80.77002, 35.37536], [-80.76961, 35.37529], [-80.76517, 35.37399], [-80.76413, 35.37375], [-80.76381, 35.3737], [-80.76228, 35.37364], [-80.76166, 35.37353], [-80.76079, 35.37315], [-80.7582, 35.37169], [-80.75623, 35.371], [-80.75575, 35.37073], [-80.75496, 35.37006], [-80.75376, 35.36863], [-80.75176, 35.36689], [-80.74977, 35.36557], [-80.74935, 35.36524], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74841, 35.36441], [-80.74807, 35.36734], [-80.74768, 35.36903], [-80.74752, 35.36956], [-80.74734, 35.36994], [-80.74705, 35.37032], [-80.74668, 35.37067], [-80.74567, 35.37137], [-80.74519, 35.37161], [-80.7433, 35.3722], [-80.74276, 35.37251], [-80.74218, 35.37306], [-80.74182, 35.37368], [-80.7397, 35.37996], [-80.7396, 35.38007], [-80.7396, 35.38007], [-80.73754, 35.37989], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.72937, 35.38327], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73029, 35.39311]]}, "date": "2012-03-13", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72865, 35.40114], [-80.73028, 35.40139], [-80.73028, 35.40139], [-80.73013, 35.40186], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.72892, 35.40821], [-80.72802, 35.40826], [-80.7229, 35.40976], [-80.7223, 35.40982], [-80.72187, 35.4098], [-80.71809, 35.4094], [-80.71772, 35.4093], [-80.71716, 35.40911], [-80.71334, 35.40745], [-80.71334, 35.40745], [-80.7117, 35.41006], [-80.71147, 35.41031], [-80.71023, 35.41139], [-80.71001, 35.41173], [-80.70982, 35.41217], [-80.70958, 35.4144], [-80.70914, 35.41998], [-80.70913, 35.42079], [-80.70932, 35.4215], [-80.71103, 35.42562], [-80.71151, 35.42623], [-80.71178, 35.42643], [-80.71209, 35.42663], [-80.71237, 35.42675], [-80.71375, 35.42715], [-80.71415, 35.42734], [-80.71627, 35.42868], [-80.71766, 35.42941], [-80.718, 35.42952], [-80.71963, 35.42982], [-80.72218, 35.43024], [-80.72277, 35.4304], [-80.72299, 35.43049], [-80.72329, 35.4307], [-80.72364, 35.43111], [-80.7238, 35.43138], [-80.72709, 35.43871], [-80.72779, 35.44124], [-80.72829, 35.44222], [-80.72956, 35.44443], [-80.72981, 35.44498], [-80.73005, 35.44569], [-80.73116, 35.45106], [-80.73178, 35.45356], [-80.73204, 35.455], [-80.73232, 35.4599], [-80.73239, 35.46039], [-80.73239, 35.46039], [-80.73192, 35.46179], [-80.7301, 35.46601], [-80.73, 35.46628], [-80.72993, 35.46673], [-80.73003, 35.4684], [-80.73073, 35.47428], [-80.73074, 35.47717], [-80.73053, 35.47774], [-80.73002, 35.47847], [-80.72737, 35.48121], [-80.72737, 35.48121], [-80.72712, 35.48104], [-80.72674, 35.48088], [-80.72635, 35.48077], [-80.72578, 35.4807], [-80.72384, 35.48068], [-80.7201, 35.48076], [-80.71857, 35.48073], [-80.71793, 35.48077], [-80.71668, 35.48107], [-80.71288, 35.48228], [-80.71197, 35.48253], [-80.71197, 35.48253], [-80.71212, 35.48275], [-80.71376, 35.4846], [-80.71392, 35.48495], [-80.71434, 35.48641], [-80.71455, 35.48695], [-80.71552, 35.48919], [-80.71587, 35.48972], [-80.71636, 35.4902], [-80.71727, 35.49087], [-80.72433, 35.49515], [-80.72491, 35.49553], [-80.7252, 35.49583], [-80.72522, 35.49599], [-80.72512, 35.49611], [-80.72426, 35.49639], [-80.72332, 35.49711], [-80.72308, 35.49716], [-80.7222, 35.49713], [-80.72111, 35.49738], [-80.72079, 35.49749], [-80.72053, 35.49764], [-80.72028, 35.49796], [-80.72023, 35.49817], [-80.72026, 35.49857], [-80.72091, 35.50204], [-80.72085, 35.50722], [-80.72085, 35.50722], [-80.72095, 35.51102], [-80.72076, 35.51262], [-80.71994, 35.51749], [-80.71985, 35.51861], [-80.71996, 35.52306], [-80.71992, 35.52714], [-80.72011, 35.53074], [-80.7201, 35.53143], [-80.72024, 35.53184], [-80.7214, 35.53365], [-80.72199, 35.53474], [-80.72249, 35.53616], [-80.72247, 35.53686], [-80.72229, 35.53768], [-80.72066, 35.54188], [-80.72013, 35.54382], [-80.72013, 35.54382], [-80.71714, 35.54435], [-80.71617, 35.54444], [-80.71303, 35.54451], [-80.71017, 35.54436], [-80.70919, 35.54426], [-80.70845, 35.54437], [-80.70689, 35.54477], [-80.70623, 35.545], [-80.70409, 35.54627], [-80.70184, 35.54724], [-80.7006, 35.54786], [-80.69996, 35.54823], [-80.69926, 35.54877], [-80.69885, 35.54916], [-80.69842, 35.54969], [-80.69796, 35.55039], [-80.69677, 35.55165], [-80.69406, 35.55412], [-80.69357, 35.55478], [-80.6932, 35.55545], [-80.69223, 35.55785], [-80.69154, 35.55874], [-80.69083, 35.55942], [-80.68902, 35.56071], [-80.68776, 35.56187], [-80.68718, 35.56256], [-80.68623, 35.56421], [-80.68359, 35.56943], [-80.68304, 35.57015], [-80.68093, 35.57231], [-80.68076, 35.57254], [-80.68076, 35.57254], [-80.68087, 35.57421], [-80.68073, 35.57475], [-80.67895, 35.579], [-80.67848, 35.58002], [-80.67784, 35.58115], [-80.67634, 35.58406], [-80.67584, 35.58454], [-80.67584, 35.58454], [-80.67506, 35.58511], [-80.67396, 35.5854], [-80.67304, 35.58547], [-80.6698, 35.58543], [-80.66837, 35.58567], [-80.66709, 35.58567], [-80.66534, 35.5863], [-80.66507, 35.58646], [-80.6647, 35.58682], [-80.66437, 35.58724], [-80.66417, 35.58739], [-80.66371, 35.58757], [-80.66105, 35.58775], [-80.66035, 35.58774], [-80.65812, 35.5873], [-80.65641, 35.58718], [-80.65565, 35.58718], [-80.6527, 35.58763], [-80.6527, 35.58763], [-80.65273, 35.58857], [-80.65327, 35.59368], [-80.65372, 35.59733], [-80.65379, 35.59857], [-80.65367, 35.6122], [-80.65364, 35.61312], [-80.65354, 35.61362], [-80.65334, 35.61426], [-80.65271, 35.61546], [-80.64453, 35.6292], [-80.64038, 35.63762], [-80.63831, 35.64166], [-80.63803, 35.64226], [-80.63778, 35.6432], [-80.63726, 35.64612], [-80.63716, 35.64686], [-80.6372, 35.64704], [-80.6372, 35.64704], [-80.63556, 35.64758], [-80.63468, 35.6477], [-80.61403, 35.64871], [-80.61361, 35.64876], [-80.61292, 35.64887], [-80.61246, 35.64899], [-80.61141, 35.64937], [-80.60984, 35.65023], [-80.60917, 35.6505], [-80.60825, 35.6507], [-80.60747, 35.65073], [-80.60498, 35.65068], [-80.60371, 35.65058], [-80.60168, 35.6503], [-80.60111, 35.65027], [-80.6001, 35.65029], [-80.59926, 35.65043], [-80.59803, 35.65076], [-80.59543, 35.65163], [-80.59442, 35.65199], [-80.59171, 35.65326], [-80.59088, 35.65358], [-80.59042, 35.6537], [-80.58962, 35.65388], [-80.58882, 35.65394], [-80.58789, 35.65392], [-80.58593, 35.6537], [-80.58593, 35.6537], [-80.58592, 35.65575], [-80.58566, 35.66328], [-80.58551, 35.66404], [-80.58425, 35.66831], [-80.58414, 35.6689], [-80.58398, 35.67186], [-80.58326, 35.67603], [-80.5831, 35.67638], [-80.58165, 35.67799], [-80.58143, 35.67846], [-80.58138, 35.67879], [-80.58132, 35.68435], [-80.58124, 35.68512], [-80.58086, 35.68621], [-80.58086, 35.68621], [-80.57986, 35.68637], [-80.56704, 35.68778], [-80.56655, 35.6878], [-80.56562, 35.68777], [-80.56426, 35.68753], [-80.56333, 35.68719], [-80.56186, 35.68652], [-80.56186, 35.68652], [-80.56176, 35.68904], [-80.56189, 35.69098], [-80.56209, 35.69245], [-80.56255, 35.69495], [-80.56258, 35.69578], [-80.56243, 35.69633], [-80.56064, 35.6988], [-80.55941, 35.70033], [-80.55719, 35.70326], [-80.55543, 35.7058], [-80.55543, 35.7058], [-80.55443, 35.70536], [-80.55384, 35.7052], [-80.55326, 35.7051], [-80.54783, 35.70477], [-80.54382, 35.70426], [-80.5432, 35.70414], [-80.5422, 35.70375], [-80.53373, 35.69957], [-80.52648, 35.69693], [-80.52563, 35.69668], [-80.51487, 35.69424], [-80.51487, 35.69424], [-80.51509, 35.69361], [-80.51509, 35.69361], [-80.51414, 35.69243], [-80.51325, 35.69196], [-80.51287, 35.6914], [-80.51233, 35.69031], [-80.51222, 35.69025], [-80.51205, 35.69025], [-80.51143, 35.69049], [-80.51143, 35.69049], [-80.51178, 35.69122]]}, "date": "2012-03-15", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72944, 35.40263], [-80.72918, 35.4031], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72956, 35.40612], [-80.72976, 35.40661], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75256, 35.42057], [-80.75294, 35.42086], [-80.7534, 35.42141], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.75628, 35.42376], [-80.75713, 35.42358], [-80.7578, 35.42352], [-80.75811, 35.42354], [-80.75839, 35.42362], [-80.75971, 35.42409], [-80.76016, 35.42421], [-80.7631, 35.42467], [-80.764, 35.42478], [-80.7644, 35.42474], [-80.76462, 35.42464], [-80.76747, 35.4226], [-80.76792, 35.42236], [-80.77201, 35.42084], [-80.77282, 35.42064], [-80.77335, 35.42061], [-80.77484, 35.42075], [-80.77551, 35.42084], [-80.77589, 35.42095], [-80.77666, 35.42129], [-80.77713, 35.42156], [-80.77754, 35.4219], [-80.77798, 35.4225], [-80.77821, 35.42269], [-80.77896, 35.42292], [-80.78116, 35.42347], [-80.78176, 35.42352], [-80.78718, 35.4232], [-80.7875, 35.42319], [-80.7879, 35.42324], [-80.78822, 35.42335], [-80.79105, 35.42488], [-80.79201, 35.42529], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79428, 35.42758], [-80.7996, 35.43312], [-80.80167, 35.43512], [-80.80274, 35.43638], [-80.80293, 35.43653], [-80.80293, 35.43653], [-80.80291, 35.43724], [-80.80286, 35.43748], [-80.80209, 35.43903], [-80.80199, 35.43932], [-80.80203, 35.43964], [-80.80255, 35.44072], [-80.80258, 35.44105], [-80.80246, 35.4415], [-80.8023, 35.44173], [-80.80188, 35.44207], [-80.80162, 35.44223], [-80.80083, 35.44253], [-80.80039, 35.44281], [-80.79874, 35.44451], [-80.79849, 35.44498], [-80.79682, 35.44892], [-80.79658, 35.4494], [-80.79601, 35.45024], [-80.79601, 35.45024], [-80.7967, 35.4507], [-80.80131, 35.4542], [-80.80201, 35.45462], [-80.80252, 35.45486], [-80.80252, 35.45486], [-80.80248, 35.45512], [-80.80255, 35.45548], [-80.80255, 35.45548], [-80.8019, 35.4561], [-80.8013, 35.45686], [-80.80091, 35.45718], [-80.7993, 35.45778], [-80.79892, 35.45788], [-80.7986, 35.4579], [-80.79796, 35.4578], [-80.79725, 35.4578], [-80.79607, 35.45794], [-80.79561, 35.45808], [-80.79484, 35.45842], [-80.79347, 35.45912], [-80.79187, 35.46022], [-80.79119, 35.46057], [-80.79083, 35.46068], [-80.79049, 35.46068], [-80.7902, 35.46056], [-80.79002, 35.46041], [-80.78961, 35.45976], [-80.78941, 35.45962], [-80.78887, 35.45958], [-80.78849, 35.45965], [-80.78713, 35.46014], [-80.78444, 35.46091], [-80.78392, 35.46096], [-80.78353, 35.46093], [-80.78317, 35.46084], [-80.78278, 35.46067], [-80.78235, 35.46033], [-80.78211, 35.46003], [-80.78173, 35.45908], [-80.78173, 35.45908], [-80.78093, 35.45929], [-80.78009, 35.4597], [-80.77975, 35.45992], [-80.77963, 35.46011], [-80.77934, 35.46216], [-80.77911, 35.46442], [-80.77914, 35.46472], [-80.77923, 35.46491], [-80.77953, 35.46526], [-80.77994, 35.46602], [-80.78114, 35.46795], [-80.78131, 35.46814], [-80.78193, 35.46861], [-80.78236, 35.4692], [-80.78294, 35.4695], [-80.78326, 35.46979], [-80.78408, 35.47125], [-80.78478, 35.473], [-80.78513, 35.47345], [-80.78513, 35.47345], [-80.78169, 35.47508], [-80.78129, 35.47516], [-80.78021, 35.47508], [-80.78021, 35.47508], [-80.77555, 35.47452], [-80.77496, 35.47451], [-80.77466, 35.47456], [-80.77385, 35.47479], [-80.77338, 35.47499], [-80.77129, 35.47627], [-80.77063, 35.47675], [-80.77051, 35.47695], [-80.77043, 35.47728], [-80.77029, 35.48141], [-80.77014, 35.48202], [-80.76692, 35.48734], [-80.76471, 35.49024], [-80.76471, 35.49024], [-80.76001, 35.48856], [-80.76001, 35.48856], [-80.75731, 35.48316], [-80.75679, 35.48229], [-80.75605, 35.48133], [-80.75561, 35.48089], [-80.75459, 35.48002], [-80.75185, 35.47785], [-80.7483, 35.4754], [-80.74705, 35.47439], [-80.74646, 35.47374], [-80.74394, 35.47036], [-80.7408, 35.46627], [-80.73887, 35.46342], [-80.73826, 35.46288], [-80.73723, 35.46219], [-80.73605, 35.46174], [-80.73293, 35.46071], [-80.73254, 35.46055], [-80.73239, 35.46039], [-80.73232, 35.4599], [-80.73204, 35.455], [-80.73178, 35.45356], [-80.73116, 35.45106], [-80.73005, 35.44569], [-80.72981, 35.44498], [-80.72956, 35.44443], [-80.72829, 35.44222], [-80.72779, 35.44124], [-80.72709, 35.43871], [-80.7238, 35.43138], [-80.72349, 35.4309], [-80.72299, 35.43049], [-80.72218, 35.43024], [-80.71963, 35.42982], [-80.718, 35.42952], [-80.71766, 35.42941], [-80.71627, 35.42868], [-80.71456, 35.42759], [-80.71387, 35.4272], [-80.71266, 35.42686], [-80.71209, 35.42663], [-80.71151, 35.42623], [-80.71103, 35.42562], [-80.70952, 35.42201], [-80.70926, 35.42133], [-80.70913, 35.42079], [-80.70914, 35.41998], [-80.70977, 35.41247], [-80.70992, 35.41192], [-80.71023, 35.41139], [-80.71147, 35.41031], [-80.7117, 35.41006], [-80.71334, 35.40745], [-80.71334, 35.40745], [-80.71716, 35.40911], [-80.71772, 35.4093], [-80.71809, 35.4094], [-80.72187, 35.4098], [-80.7223, 35.40982], [-80.7229, 35.40976], [-80.72802, 35.40826], [-80.72892, 35.40821], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73068, 35.40803], [-80.72976, 35.40661], [-80.72956, 35.40612], [-80.72908, 35.40393], [-80.72912, 35.40332], [-80.72923, 35.40296], [-80.72939, 35.40268]]}, "date": "2012-03-20", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75256, 35.42057], [-80.75294, 35.42086], [-80.7534, 35.42141], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.75628, 35.42376], [-80.75713, 35.42358], [-80.7578, 35.42352], [-80.75811, 35.42354], [-80.75839, 35.42362], [-80.75971, 35.42409], [-80.76016, 35.42421], [-80.7631, 35.42467], [-80.764, 35.42478], [-80.7644, 35.42474], [-80.76462, 35.42464], [-80.76747, 35.4226], [-80.76792, 35.42236], [-80.77201, 35.42084], [-80.77282, 35.42064], [-80.77335, 35.42061], [-80.77484, 35.42075], [-80.77551, 35.42084], [-80.77589, 35.42095], [-80.77666, 35.42129], [-80.77713, 35.42156], [-80.77754, 35.4219], [-80.77798, 35.4225], [-80.77821, 35.42269], [-80.77896, 35.42292], [-80.78116, 35.42347], [-80.78176, 35.42352], [-80.78718, 35.4232], [-80.7875, 35.42319], [-80.7879, 35.42324], [-80.78822, 35.42335], [-80.79105, 35.42488], [-80.79201, 35.42529], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79406, 35.42406], [-80.79483, 35.42324], [-80.79732, 35.42077], [-80.79863, 35.41961], [-80.79885, 35.41926], [-80.80057, 35.41562], [-80.80089, 35.41512], [-80.80186, 35.41438], [-80.8022, 35.41425], [-80.80461, 35.41366], [-80.80461, 35.41366], [-80.80471, 35.41173], [-80.80469, 35.41138], [-80.80462, 35.41114], [-80.80437, 35.41073], [-80.80356, 35.40982], [-80.80334, 35.40933], [-80.8031, 35.40852], [-80.80282, 35.40789], [-80.80251, 35.40739], [-80.80203, 35.40691], [-80.80179, 35.40653], [-80.80133, 35.4049], [-80.80126, 35.40431], [-80.80129, 35.40417], [-80.80147, 35.40387], [-80.80186, 35.40341], [-80.80349, 35.40201], [-80.80366, 35.40169], [-80.80368, 35.40065], [-80.8035, 35.3993], [-80.80238, 35.39644], [-80.80226, 35.39591], [-80.80234, 35.39557], [-80.80272, 35.3948], [-80.80313, 35.39375], [-80.80431, 35.39016], [-80.80459, 35.38905], [-80.80465, 35.38868], [-80.80463, 35.38838], [-80.80426, 35.38685], [-80.8042, 35.38641], [-80.80425, 35.3853], [-80.80437, 35.38469], [-80.80457, 35.3844], [-80.80485, 35.38417], [-80.80551, 35.38377], [-80.80646, 35.38332], [-80.80968, 35.38193], [-80.80989, 35.38181], [-80.80998, 35.38165], [-80.80999, 35.38137], [-80.80989, 35.38108], [-80.80877, 35.37857], [-80.80872, 35.37813], [-80.80881, 35.37619], [-80.80859, 35.37423], [-80.80839, 35.37338], [-80.80821, 35.37295], [-80.8077, 35.37243], [-80.80745, 35.37203], [-80.80745, 35.37203], [-80.80574, 35.37294], [-80.80313, 35.37445], [-80.7944, 35.3804], [-80.79396, 35.38059], [-80.78977, 35.38192], [-80.78905, 35.38232], [-80.78813, 35.383], [-80.78686, 35.38424], [-80.78668, 35.38429], [-80.78586, 35.38513], [-80.78416, 35.3867], [-80.77943, 35.39042], [-80.7786, 35.39092], [-80.77556, 35.39213], [-80.77556, 35.39213], [-80.77503, 35.39166], [-80.77113, 35.38892], [-80.76918, 35.38759], [-80.76869, 35.38736], [-80.7676, 35.38716], [-80.76564, 35.38689], [-80.76521, 35.38686], [-80.76456, 35.3869], [-80.76378, 35.38712], [-80.76079, 35.38836], [-80.76018, 35.38853], [-80.75968, 35.38859], [-80.75927, 35.38859], [-80.75855, 35.38851], [-80.75789, 35.38828], [-80.75704, 35.38776], [-80.75664, 35.38732], [-80.75664, 35.38732], [-80.75512, 35.3881], [-80.75403, 35.38847], [-80.75355, 35.38855], [-80.75299, 35.38857], [-80.75129, 35.38849], [-80.75096, 35.38852], [-80.75016, 35.38874], [-80.75016, 35.38874], [-80.74955, 35.3877], [-80.74936, 35.38745], [-80.74917, 35.3873], [-80.74885, 35.38715], [-80.74856, 35.38708], [-80.74771, 35.38701], [-80.74716, 35.38692], [-80.74689, 35.38677], [-80.74671, 35.38659], [-80.74637, 35.38565], [-80.74569, 35.38468], [-80.74553, 35.38428], [-80.74554, 35.38408], [-80.74606, 35.38293], [-80.74609, 35.38274], [-80.74604, 35.38234], [-80.74587, 35.38206], [-80.74532, 35.38153], [-80.74514, 35.38118], [-80.74514, 35.38118], [-80.74515, 35.38104], [-80.74501, 35.38098], [-80.74489, 35.38105], [-80.74487, 35.38112], [-80.74446, 35.38125], [-80.74417, 35.38128], [-80.74362, 35.38123], [-80.74006, 35.38016], [-80.7396, 35.38007], [-80.73766, 35.37992], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.72937, 35.38327], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73029, 35.39311]]}, "date": "2012-03-21", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75256, 35.42057], [-80.75294, 35.42086], [-80.7534, 35.42141], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.75628, 35.42376], [-80.75713, 35.42358], [-80.7578, 35.42352], [-80.75811, 35.42354], [-80.75839, 35.42362], [-80.75971, 35.42409], [-80.76016, 35.42421], [-80.7631, 35.42467], [-80.764, 35.42478], [-80.7644, 35.42474], [-80.76462, 35.42464], [-80.76747, 35.4226], [-80.76792, 35.42236], [-80.77201, 35.42084], [-80.77282, 35.42064], [-80.77335, 35.42061], [-80.77484, 35.42075], [-80.77551, 35.42084], [-80.77589, 35.42095], [-80.77666, 35.42129], [-80.77713, 35.42156], [-80.77754, 35.4219], [-80.77798, 35.4225], [-80.77821, 35.42269], [-80.77896, 35.42292], [-80.78116, 35.42347], [-80.78176, 35.42352], [-80.78718, 35.4232], [-80.7875, 35.42319], [-80.7879, 35.42324], [-80.78822, 35.42335], [-80.79105, 35.42488], [-80.79201, 35.42529], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79406, 35.42406], [-80.79483, 35.42324], [-80.79732, 35.42077], [-80.79863, 35.41961], [-80.79885, 35.41926], [-80.80057, 35.41562], [-80.80089, 35.41512], [-80.80186, 35.41438], [-80.8022, 35.41425], [-80.80461, 35.41366], [-80.80461, 35.41366], [-80.80471, 35.41173], [-80.80469, 35.41138], [-80.80462, 35.41114], [-80.80437, 35.41073], [-80.80356, 35.40982], [-80.80334, 35.40933], [-80.8031, 35.40852], [-80.80282, 35.40789], [-80.80251, 35.40739], [-80.80203, 35.40691], [-80.80179, 35.40653], [-80.80133, 35.4049], [-80.80126, 35.40431], [-80.80129, 35.40417], [-80.80147, 35.40387], [-80.80186, 35.40341], [-80.80349, 35.40201], [-80.80366, 35.40169], [-80.80368, 35.40065], [-80.8035, 35.3993], [-80.80238, 35.39644], [-80.80226, 35.39591], [-80.80234, 35.39557], [-80.80272, 35.3948], [-80.80313, 35.39375], [-80.80431, 35.39016], [-80.80459, 35.38905], [-80.80465, 35.38868], [-80.80463, 35.38838], [-80.80426, 35.38685], [-80.8042, 35.38641], [-80.80425, 35.3853], [-80.80437, 35.38469], [-80.80457, 35.3844], [-80.80485, 35.38417], [-80.80551, 35.38377], [-80.80646, 35.38332], [-80.80968, 35.38193], [-80.80989, 35.38181], [-80.80998, 35.38165], [-80.80999, 35.38137], [-80.80989, 35.38108], [-80.80877, 35.37857], [-80.80872, 35.37813], [-80.80881, 35.37619], [-80.80859, 35.37423], [-80.80839, 35.37338], [-80.80821, 35.37295], [-80.8077, 35.37243], [-80.80745, 35.37203], [-80.80745, 35.37203], [-80.80698, 35.37142], [-80.80677, 35.3708], [-80.80676, 35.37058], [-80.807, 35.36902], [-80.80689, 35.36846], [-80.80676, 35.36821], [-80.80606, 35.36751], [-80.80531, 35.36689], [-80.80434, 35.36553], [-80.80401, 35.36514], [-80.8035, 35.36483], [-80.80261, 35.36458], [-80.80218, 35.36438], [-80.80105, 35.36326], [-80.80062, 35.36265], [-80.80048, 35.36233], [-80.79995, 35.36032], [-80.79995, 35.36032], [-80.79743, 35.36106], [-80.79256, 35.36327], [-80.78935, 35.3644], [-80.7887, 35.3647], [-80.78749, 35.36537], [-80.78749, 35.36537], [-80.78754, 35.36569], [-80.7875, 35.36598], [-80.7873, 35.36639], [-80.78683, 35.36795], [-80.78572, 35.36951], [-80.78572, 35.36951], [-80.78456, 35.36876], [-80.78279, 35.36772], [-80.78234, 35.36764], [-80.7814, 35.36765], [-80.78109, 35.36755], [-80.78092, 35.36737], [-80.78059, 35.36674], [-80.7802, 35.36629], [-80.78004, 35.3662], [-80.7797, 35.36612], [-80.77551, 35.36575], [-80.77443, 35.36548], [-80.77298, 35.36531], [-80.77247, 35.36538], [-80.76979, 35.36604], [-80.76935, 35.36623], [-80.76785, 35.36709], [-80.76744, 35.36714], [-80.76591, 35.36699], [-80.76548, 35.36689], [-80.76493, 35.36658], [-80.76309, 35.36576], [-80.76279, 35.36555], [-80.76265, 35.36538], [-80.76204, 35.36368], [-80.7617, 35.36315], [-80.76137, 35.3628], [-80.76105, 35.36255], [-80.76022, 35.36206], [-80.75895, 35.361], [-80.75838, 35.36025], [-80.75806, 35.35883], [-80.7575, 35.35807], [-80.75734, 35.3577], [-80.75725, 35.35719], [-80.7572, 35.35573], [-80.75711, 35.35552], [-80.7569, 35.35532], [-80.7569, 35.35532], [-80.75642, 35.35627], [-80.75584, 35.35695], [-80.75314, 35.35892], [-80.75269, 35.35911], [-80.74949, 35.35954], [-80.74949, 35.35954], [-80.74897, 35.36103], [-80.74872, 35.36212], [-80.74846, 35.36343], [-80.74844, 35.36387], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74841, 35.36441], [-80.74807, 35.36734], [-80.74768, 35.36903], [-80.74752, 35.36956], [-80.74734, 35.36994], [-80.74705, 35.37032], [-80.74668, 35.37067], [-80.74567, 35.37137], [-80.74519, 35.37161], [-80.7433, 35.3722], [-80.74276, 35.37251], [-80.74218, 35.37306], [-80.74182, 35.37368], [-80.7397, 35.37996], [-80.7396, 35.38007], [-80.7396, 35.38007], [-80.73754, 35.37989], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.72937, 35.38327], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73029, 35.39311]]}, "date": "2012-03-23", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72939, 35.40268], [-80.72923, 35.40296], [-80.72912, 35.40332], [-80.72908, 35.40393], [-80.72956, 35.40612], [-80.72976, 35.40661], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73325, 35.40864], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74153, 35.41507], [-80.74153, 35.41507], [-80.741, 35.41553], [-80.741, 35.41553], [-80.7411, 35.41562], [-80.74167, 35.41518], [-80.74167, 35.41518], [-80.73984, 35.41359], [-80.73526, 35.41053], [-80.73379, 35.40906], [-80.73325, 35.40864], [-80.73263, 35.40842], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73068, 35.40803], [-80.73004, 35.40709], [-80.72969, 35.40648], [-80.72956, 35.40612], [-80.72945, 35.4055], [-80.72912, 35.40425], [-80.72908, 35.40376], [-80.72918, 35.4031], [-80.72939, 35.40268], [-80.72978, 35.40226]]}, "date": "2012-03-24", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.66288, 35.45129], [-80.66247, 35.45151], [-80.66246, 35.45165], [-80.66518, 35.45424], [-80.66556, 35.4547], [-80.66573, 35.45499], [-80.66573, 35.45499], [-80.66558, 35.45507], [-80.66445, 35.45622], [-80.66411, 35.45673], [-80.66389, 35.45721], [-80.66363, 35.45817], [-80.6636, 35.45874], [-80.66364, 35.45928], [-80.66379, 35.45991], [-80.66401, 35.46045], [-80.66442, 35.46113], [-80.66788, 35.46542], [-80.66973, 35.46783], [-80.66997, 35.46835], [-80.67019, 35.46912], [-80.67037, 35.47146], [-80.67057, 35.47234], [-80.67256, 35.47644], [-80.67285, 35.47734], [-80.67382, 35.48148], [-80.67384, 35.48234], [-80.67375, 35.48282], [-80.67273, 35.48582], [-80.67164, 35.48877], [-80.67139, 35.48993], [-80.67139, 35.48993], [-80.66997, 35.48957], [-80.66574, 35.48893], [-80.66574, 35.48893], [-80.66582, 35.48937], [-80.66603, 35.48983], [-80.66732, 35.49162], [-80.66782, 35.49245], [-80.66882, 35.49375], [-80.6705, 35.49643], [-80.67103, 35.49719], [-80.67167, 35.49778], [-80.67427, 35.49953], [-80.675, 35.50012], [-80.67543, 35.50064], [-80.67569, 35.50124], [-80.67576, 35.50174], [-80.67536, 35.50502], [-80.67537, 35.5057], [-80.67546, 35.50651], [-80.67546, 35.50651], [-80.67723, 35.50554], [-80.67815, 35.50522], [-80.67942, 35.505], [-80.68174, 35.5048], [-80.68501, 35.50429], [-80.68501, 35.50429], [-80.68404, 35.50083], [-80.6834, 35.49892], [-80.68315, 35.49846], [-80.68286, 35.49815], [-80.68243, 35.49797], [-80.68105, 35.49786], [-80.68022, 35.49774], [-80.67999, 35.49766], [-80.67965, 35.49748], [-80.67903, 35.49702], [-80.67858, 35.49657], [-80.67829, 35.49577], [-80.67802, 35.4946], [-80.67797, 35.494], [-80.67794, 35.49136], [-80.67789, 35.49096], [-80.67789, 35.49096], [-80.67733, 35.49105], [-80.67663, 35.49108], [-80.67561, 35.49098], [-80.67168, 35.49], [-80.67168, 35.49], [-80.67193, 35.48881], [-80.67378, 35.48357], [-80.67397, 35.48285], [-80.67408, 35.48202], [-80.67403, 35.48143], [-80.67306, 35.47712], [-80.67283, 35.47643], [-80.67129, 35.47322], [-80.67086, 35.47243], [-80.67069, 35.47194], [-80.67059, 35.47145], [-80.67042, 35.46914], [-80.67025, 35.46847], [-80.67002, 35.46791], [-80.66966, 35.46734], [-80.66882, 35.46621], [-80.66809, 35.46536], [-80.6676, 35.46466], [-80.66645, 35.46323], [-80.66569, 35.46239], [-80.66468, 35.4611], [-80.66421, 35.46035], [-80.66399, 35.45983], [-80.66387, 35.45938], [-80.66381, 35.45877], [-80.66384, 35.45828], [-80.66398, 35.45758], [-80.66446, 35.4566], [-80.66487, 35.45602], [-80.66487, 35.45602], [-80.66888, 35.45238], [-80.67001, 35.45124], [-80.67303, 35.44843], [-80.67385, 35.44772], [-80.67667, 35.44501], [-80.67812, 35.44324], [-80.67902, 35.44175], [-80.67947, 35.44081], [-80.68004, 35.4394], [-80.68004, 35.4394], [-80.68145, 35.43974], [-80.68217, 35.43997], [-80.68299, 35.44035], [-80.68395, 35.441], [-80.68395, 35.441], [-80.6836, 35.44175], [-80.68287, 35.44395], [-80.68273, 35.44445], [-80.68266, 35.44541], [-80.68339, 35.44929], [-80.68413, 35.45078], [-80.68594, 35.454], [-80.68638, 35.45485], [-80.68647, 35.45513], [-80.68715, 35.4592], [-80.68787, 35.46183], [-80.68787, 35.4622], [-80.68777, 35.46248], [-80.68717, 35.46356], [-80.68701, 35.46377], [-80.68688, 35.46385], [-80.68688, 35.46385], [-80.68587, 35.46252], [-80.68183, 35.45779], [-80.6777, 35.45461], [-80.67692, 35.45408], [-80.67393, 35.45236], [-80.67281, 35.45196], [-80.66977, 35.45119], [-80.66977, 35.45119], [-80.66677, 35.45405], [-80.66677, 35.45405], [-80.66596, 35.45477], [-80.66596, 35.45477], [-80.66556, 35.4547], [-80.66518, 35.45424], [-80.66246, 35.45165], [-80.66247, 35.45151], [-80.66285, 35.45131]]}, "date": "2012-03-25", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74541, 35.41836], [-80.74572, 35.41852], [-80.74572, 35.41852], [-80.74711, 35.41763], [-80.75152, 35.41452], [-80.75224, 35.41397], [-80.75258, 35.41364], [-80.75295, 35.41311], [-80.75351, 35.41216], [-80.75552, 35.40702], [-80.75588, 35.40632], [-80.75639, 35.40549], [-80.75699, 35.40481], [-80.75744, 35.4044], [-80.75998, 35.40249], [-80.76106, 35.40183], [-80.76106, 35.40183], [-80.76032, 35.40094], [-80.75992, 35.40059], [-80.75956, 35.4004], [-80.75877, 35.40014], [-80.75822, 35.39986], [-80.75749, 35.3992], [-80.75707, 35.39894], [-80.75504, 35.39795], [-80.75449, 35.39763], [-80.7536, 35.39685], [-80.75323, 35.39666], [-80.75276, 35.39655], [-80.75142, 35.39637], [-80.75086, 35.39625], [-80.75045, 35.39608], [-80.74982, 35.39571], [-80.74911, 35.39537], [-80.74704, 35.39472], [-80.74671, 35.39453], [-80.74625, 35.39409], [-80.74604, 35.39379], [-80.74587, 35.39336], [-80.74583, 35.39308], [-80.74592, 35.3924], [-80.74646, 35.39124], [-80.74669, 35.39084], [-80.74686, 35.39064], [-80.74738, 35.39027], [-80.74847, 35.38974], [-80.74964, 35.38898], [-80.75016, 35.38874], [-80.75096, 35.38852], [-80.75149, 35.38849], [-80.75299, 35.38857], [-80.75429, 35.38847], [-80.75516, 35.38819], [-80.75671, 35.38742], [-80.75671, 35.38742], [-80.7563, 35.38668], [-80.75565, 35.38518], [-80.75508, 35.38432], [-80.75413, 35.38323], [-80.75399, 35.38279], [-80.75402, 35.38243], [-80.75542, 35.37886], [-80.75563, 35.37846], [-80.75585, 35.37819], [-80.75632, 35.37781], [-80.75696, 35.37751], [-80.75735, 35.3774], [-80.75805, 35.37736], [-80.75844, 35.37739], [-80.75888, 35.3775], [-80.76196, 35.37867], [-80.76381, 35.37974], [-80.76381, 35.37974], [-80.76444, 35.37902], [-80.7651, 35.37744], [-80.76506, 35.3772], [-80.76489, 35.37696], [-80.76443, 35.37646], [-80.76395, 35.37603], [-80.7638, 35.3758], [-80.76374, 35.37527], [-80.76381, 35.3737], [-80.76381, 35.3737], [-80.76228, 35.37364], [-80.7615, 35.37348], [-80.76066, 35.37308], [-80.75893, 35.37207], [-80.7582, 35.37169], [-80.75623, 35.371], [-80.75591, 35.37084], [-80.75542, 35.37048], [-80.75496, 35.37006], [-80.75376, 35.36863], [-80.75176, 35.36689], [-80.74977, 35.36557], [-80.74935, 35.36524], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74841, 35.36441], [-80.74807, 35.36734], [-80.74768, 35.36903], [-80.74752, 35.36956], [-80.74734, 35.36994], [-80.74705, 35.37032], [-80.74668, 35.37067], [-80.74567, 35.37137], [-80.74519, 35.37161], [-80.7433, 35.3722], [-80.74276, 35.37251], [-80.74218, 35.37306], [-80.74182, 35.37368], [-80.7397, 35.37996], [-80.7396, 35.38007], [-80.7396, 35.38007], [-80.73754, 35.37989], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.72937, 35.38327], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73029, 35.39311]]}, "date": "2012-03-26", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.73029, 35.3936], [-80.73051, 35.39643], [-80.73066, 35.3974], [-80.73086, 35.39825], [-80.73088, 35.39884], [-80.73019, 35.40173], [-80.72999, 35.40205], [-80.72939, 35.40268], [-80.72923, 35.40296], [-80.7291, 35.40344], [-80.72908, 35.40393], [-80.72961, 35.40628], [-80.73004, 35.40709], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75277, 35.42072], [-80.75314, 35.42107], [-80.75352, 35.42159], [-80.75726, 35.42794], [-80.75893, 35.42995], [-80.75928, 35.43016], [-80.75977, 35.43028], [-80.75977, 35.43028], [-80.76035, 35.43043], [-80.7609, 35.43069], [-80.7615, 35.43114], [-80.76222, 35.43187], [-80.76251, 35.43225], [-80.76261, 35.43246], [-80.76266, 35.43278], [-80.76265, 35.43332], [-80.76279, 35.43377], [-80.76308, 35.43413], [-80.76379, 35.43464], [-80.76412, 35.43503], [-80.76434, 35.43565], [-80.76441, 35.4362], [-80.76453, 35.43788], [-80.76452, 35.4382], [-80.76442, 35.43866], [-80.76442, 35.43866], [-80.76392, 35.43969], [-80.76382, 35.4401], [-80.76386, 35.44109], [-80.76401, 35.44217], [-80.76426, 35.44283], [-80.7658, 35.4448], [-80.76597, 35.44519], [-80.76602, 35.44555], [-80.76596, 35.44596], [-80.76568, 35.44642], [-80.76487, 35.44716], [-80.76418, 35.44801], [-80.76401, 35.44829], [-80.76397, 35.44856], [-80.76409, 35.44928], [-80.76409, 35.44955], [-80.76402, 35.45106], [-80.76393, 35.4515], [-80.76371, 35.45188], [-80.76308, 35.45271], [-80.76264, 35.45402], [-80.76246, 35.45445], [-80.76209, 35.4551], [-80.76173, 35.45558], [-80.76115, 35.45618], [-80.75998, 35.45718], [-80.75959, 35.45762], [-80.75939, 35.45793], [-80.75932, 35.45819], [-80.75922, 35.45922], [-80.75855, 35.46121], [-80.75779, 35.46275], [-80.75738, 35.46478], [-80.75633, 35.46824], [-80.75619, 35.46857], [-80.75016, 35.47668], [-80.75016, 35.47668], [-80.75134, 35.47748], [-80.75278, 35.47857], [-80.75522, 35.48055], [-80.75605, 35.48133], [-80.75654, 35.48194], [-80.75707, 35.48274], [-80.75871, 35.48597], [-80.75871, 35.48597], [-80.76024, 35.48539], [-80.76524, 35.48337], [-80.76614, 35.48306], [-80.76733, 35.48285], [-80.76771, 35.48283], [-80.76961, 35.48291], [-80.76961, 35.48291], [-80.77014, 35.48202], [-80.77025, 35.48167], [-80.77033, 35.48068], [-80.77043, 35.47728], [-80.77051, 35.47695], [-80.77063, 35.47675], [-80.77096, 35.47649], [-80.77351, 35.47492], [-80.77413, 35.4747], [-80.77496, 35.47451], [-80.77616, 35.47458], [-80.78021, 35.47508], [-80.78021, 35.47508], [-80.78108, 35.47516], [-80.7814, 35.47515], [-80.78169, 35.47508], [-80.78513, 35.47345], [-80.78513, 35.47345], [-80.78478, 35.473], [-80.78408, 35.47125], [-80.78326, 35.46979], [-80.78294, 35.4695], [-80.78236, 35.4692], [-80.78193, 35.46861], [-80.78131, 35.46814], [-80.78114, 35.46795], [-80.77994, 35.46602], [-80.77953, 35.46526], [-80.77923, 35.46491], [-80.77914, 35.46472], [-80.77911, 35.46442], [-80.77934, 35.46216], [-80.77963, 35.46011], [-80.77975, 35.45992], [-80.78009, 35.4597], [-80.78093, 35.45929], [-80.78173, 35.45908], [-80.78173, 35.45908], [-80.78211, 35.46003], [-80.78235, 35.46033], [-80.78278, 35.46067], [-80.78317, 35.46084], [-80.78353, 35.46093], [-80.78392, 35.46096], [-80.78444, 35.46091], [-80.78713, 35.46014], [-80.78849, 35.45965], [-80.78887, 35.45958], [-80.78941, 35.45962], [-80.78961, 35.45976], [-80.79002, 35.46041], [-80.7902, 35.46056], [-80.79049, 35.46068], [-80.79083, 35.46068], [-80.79119, 35.46057], [-80.79187, 35.46022], [-80.79347, 35.45912], [-80.79484, 35.45842], [-80.79561, 35.45808], [-80.79607, 35.45794], [-80.79725, 35.4578], [-80.79796, 35.4578], [-80.7986, 35.4579], [-80.79892, 35.45788], [-80.7993, 35.45778], [-80.80091, 35.45718], [-80.8013, 35.45686], [-80.8019, 35.4561], [-80.80255, 35.45548], [-80.80255, 35.45548], [-80.80249, 35.4552], [-80.80262, 35.45501], [-80.8028, 35.45496], [-80.8028, 35.45496], [-80.80201, 35.45462], [-80.80131, 35.4542], [-80.7967, 35.4507], [-80.79601, 35.45024], [-80.79601, 35.45024], [-80.79658, 35.4494], [-80.79682, 35.44892], [-80.79849, 35.44498], [-80.79874, 35.44451], [-80.80039, 35.44281], [-80.80083, 35.44253], [-80.80162, 35.44223], [-80.80188, 35.44207], [-80.8023, 35.44173], [-80.80246, 35.4415], [-80.80258, 35.44105], [-80.80255, 35.44072], [-80.80203, 35.43964], [-80.80199, 35.43932], [-80.80209, 35.43903], [-80.80286, 35.43748], [-80.80291, 35.43724], [-80.80293, 35.43653], [-80.80293, 35.43653], [-80.80274, 35.43638], [-80.80167, 35.43512], [-80.7996, 35.43312], [-80.79428, 35.42758], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79406, 35.42406], [-80.79483, 35.42324], [-80.79732, 35.42077], [-80.79863, 35.41961], [-80.79885, 35.41926], [-80.80057, 35.41562], [-80.80089, 35.41512], [-80.80186, 35.41438], [-80.8022, 35.41425], [-80.80461, 35.41366], [-80.80461, 35.41366], [-80.80471, 35.41173], [-80.80469, 35.41138], [-80.80462, 35.41114], [-80.80437, 35.41073], [-80.80356, 35.40982], [-80.80334, 35.40933], [-80.8031, 35.40852], [-80.80282, 35.40789], [-80.80251, 35.40739], [-80.80203, 35.40691], [-80.80179, 35.40653], [-80.80133, 35.4049], [-80.80126, 35.40431], [-80.80129, 35.40417], [-80.80147, 35.40387], [-80.80186, 35.40341], [-80.80349, 35.40201], [-80.80366, 35.40169], [-80.80368, 35.40065], [-80.8035, 35.3993], [-80.80238, 35.39644], [-80.80226, 35.39591], [-80.80234, 35.39557], [-80.80272, 35.3948], [-80.80313, 35.39375], [-80.80431, 35.39016], [-80.80459, 35.38905], [-80.80465, 35.38868], [-80.80463, 35.38838], [-80.80426, 35.38685], [-80.8042, 35.38641], [-80.80425, 35.3853], [-80.80437, 35.38469], [-80.80457, 35.3844], [-80.80485, 35.38417], [-80.80551, 35.38377], [-80.80646, 35.38332], [-80.80968, 35.38193], [-80.80989, 35.38181], [-80.80998, 35.38165], [-80.80999, 35.38137], [-80.80989, 35.38108], [-80.80877, 35.37857], [-80.80872, 35.37813], [-80.80881, 35.37619], [-80.80859, 35.37423], [-80.80839, 35.37338], [-80.80821, 35.37295], [-80.8077, 35.37243], [-80.80745, 35.37203], [-80.80745, 35.37203], [-80.80574, 35.37294], [-80.80313, 35.37445], [-80.7944, 35.3804], [-80.79396, 35.38059], [-80.78991, 35.38187], [-80.78948, 35.38206], [-80.78844, 35.38275], [-80.78813, 35.383], [-80.78686, 35.38424], [-80.78668, 35.38429], [-80.78668, 35.38429], [-80.78628, 35.38251], [-80.7859, 35.3803], [-80.78542, 35.37673], [-80.78482, 35.37283], [-80.78482, 35.37283], [-80.78339, 35.37296], [-80.78295, 35.3729], [-80.7817, 35.37251], [-80.78139, 35.37247], [-80.78075, 35.37251], [-80.77909, 35.37297], [-80.77405, 35.37449], [-80.77076, 35.37531], [-80.77037, 35.37537], [-80.77002, 35.37536], [-80.76961, 35.37529], [-80.76517, 35.37399], [-80.76413, 35.37375], [-80.76381, 35.3737], [-80.76241, 35.37365], [-80.76186, 35.37358], [-80.76125, 35.37338], [-80.76079, 35.37315], [-80.7582, 35.37169], [-80.75623, 35.371], [-80.75575, 35.37073], [-80.75496, 35.37006], [-80.75376, 35.36863], [-80.75176, 35.36689], [-80.74977, 35.36557], [-80.74935, 35.36524], [-80.74851, 35.36414], [-80.74851, 35.36414], [-80.74841, 35.36441], [-80.74807, 35.36734], [-80.74768, 35.36903], [-80.74752, 35.36956], [-80.74734, 35.36994], [-80.74705, 35.37032], [-80.74668, 35.37067], [-80.74567, 35.37137], [-80.74519, 35.37161], [-80.7433, 35.3722], [-80.74276, 35.37251], [-80.74218, 35.37306], [-80.74182, 35.37368], [-80.7397, 35.37996], [-80.7396, 35.38007], [-80.7396, 35.38007], [-80.73754, 35.37989], [-80.73724, 35.37978], [-80.7365, 35.3794], [-80.73565, 35.37884], [-80.73565, 35.37884], [-80.73496, 35.37958], [-80.73431, 35.38006], [-80.73298, 35.38066], [-80.73168, 35.38118], [-80.73056, 35.3817], [-80.72993, 35.38223], [-80.72947, 35.38294], [-80.72937, 35.38327], [-80.72904, 35.38526], [-80.72899, 35.38591], [-80.72911, 35.38634], [-80.72996, 35.38803], [-80.7302, 35.38882], [-80.73029, 35.39311]]}, "date": "2012-03-27", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72939, 35.40268], [-80.72923, 35.40296], [-80.72912, 35.40332], [-80.72908, 35.40393], [-80.72956, 35.40612], [-80.72976, 35.40661], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75277, 35.42072], [-80.75314, 35.42107], [-80.75352, 35.42159], [-80.75726, 35.42794], [-80.75893, 35.42995], [-80.75928, 35.43016], [-80.75977, 35.43028], [-80.75977, 35.43028], [-80.76035, 35.43043], [-80.7609, 35.43069], [-80.7615, 35.43114], [-80.76222, 35.43187], [-80.76251, 35.43225], [-80.76261, 35.43246], [-80.76266, 35.43278], [-80.76265, 35.43332], [-80.76279, 35.43377], [-80.76308, 35.43413], [-80.76379, 35.43464], [-80.76412, 35.43503], [-80.76434, 35.43565], [-80.76441, 35.4362], [-80.76453, 35.43788], [-80.76452, 35.4382], [-80.76442, 35.43866], [-80.76442, 35.43866], [-80.76392, 35.43969], [-80.76382, 35.4401], [-80.76386, 35.44109], [-80.76386, 35.44109], [-80.75953, 35.44138], [-80.74873, 35.4422], [-80.74757, 35.4422], [-80.74077, 35.44183], [-80.73907, 35.44168], [-80.73599, 35.4413], [-80.71472, 35.43856], [-80.71319, 35.43832], [-80.70981, 35.43753], [-80.7088, 35.43736], [-80.70755, 35.43728], [-80.70671, 35.43728], [-80.70461, 35.43741], [-80.70395, 35.43741], [-80.7034, 35.43738], [-80.70253, 35.43725], [-80.70253, 35.43725], [-80.70259, 35.43678], [-80.70244, 35.43408], [-80.70247, 35.43377], [-80.7039, 35.43077], [-80.70482, 35.42908], [-80.70551, 35.42677], [-80.70551, 35.42677], [-80.70495, 35.42666], [-80.70472, 35.42659], [-80.70449, 35.42646], [-80.70351, 35.42533], [-80.70326, 35.42513], [-80.70272, 35.42493], [-80.70188, 35.42477], [-80.70188, 35.42477], [-80.70207, 35.42401], [-80.70291, 35.41886], [-80.70312, 35.41831], [-80.7034, 35.4179], [-80.70426, 35.41721], [-80.70548, 35.41678], [-80.7063, 35.41639], [-80.7069, 35.41606], [-80.70758, 35.41552], [-80.70799, 35.41526], [-80.70958, 35.4144], [-80.70958, 35.4144], [-80.70977, 35.41247], [-80.70982, 35.41217], [-80.71001, 35.41173], [-80.71023, 35.41139], [-80.71147, 35.41031], [-80.7117, 35.41006], [-80.71334, 35.40745], [-80.71334, 35.40745], [-80.71716, 35.40911], [-80.71772, 35.4093], [-80.71809, 35.4094], [-80.72187, 35.4098], [-80.7223, 35.40982], [-80.7229, 35.40976], [-80.72802, 35.40826], [-80.72892, 35.40821], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73068, 35.40803], [-80.73004, 35.40709], [-80.72969, 35.40648], [-80.72956, 35.40612], [-80.72945, 35.4055], [-80.72912, 35.40425], [-80.72908, 35.40376], [-80.72918, 35.4031], [-80.72939, 35.40268], [-80.72978, 35.40226]]}, "date": "2012-03-28", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72939, 35.40268], [-80.72923, 35.40296], [-80.72912, 35.40332], [-80.72908, 35.40393], [-80.72956, 35.40612], [-80.72976, 35.40661], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75277, 35.42072], [-80.75314, 35.42107], [-80.75352, 35.42159], [-80.75726, 35.42794], [-80.75893, 35.42995], [-80.75928, 35.43016], [-80.75977, 35.43028], [-80.75977, 35.43028], [-80.76035, 35.43043], [-80.7609, 35.43069], [-80.7615, 35.43114], [-80.76222, 35.43187], [-80.76251, 35.43225], [-80.76261, 35.43246], [-80.76266, 35.43278], [-80.76265, 35.43332], [-80.76279, 35.43377], [-80.76308, 35.43413], [-80.76379, 35.43464], [-80.76412, 35.43503], [-80.76434, 35.43565], [-80.76441, 35.4362], [-80.76453, 35.43788], [-80.76452, 35.4382], [-80.76442, 35.43866], [-80.76442, 35.43866], [-80.76392, 35.43969], [-80.76382, 35.4401], [-80.76386, 35.44109], [-80.76401, 35.44217], [-80.76426, 35.44283], [-80.7658, 35.4448], [-80.76597, 35.44519], [-80.76602, 35.44555], [-80.76596, 35.44596], [-80.76568, 35.44642], [-80.76487, 35.44716], [-80.76418, 35.44801], [-80.76401, 35.44829], [-80.76397, 35.44856], [-80.76409, 35.44928], [-80.76409, 35.44955], [-80.76402, 35.45106], [-80.76393, 35.4515], [-80.76371, 35.45188], [-80.76308, 35.45271], [-80.76264, 35.45402], [-80.76246, 35.45445], [-80.76209, 35.4551], [-80.76173, 35.45558], [-80.76115, 35.45618], [-80.75998, 35.45718], [-80.75959, 35.45762], [-80.75939, 35.45793], [-80.75932, 35.45819], [-80.75922, 35.45922], [-80.75855, 35.46121], [-80.75779, 35.46275], [-80.75738, 35.46478], [-80.75633, 35.46824], [-80.75619, 35.46857], [-80.75016, 35.47668], [-80.75016, 35.47668], [-80.7483, 35.4754], [-80.74736, 35.47467], [-80.74646, 35.47374], [-80.74646, 35.47374], [-80.74571, 35.47408], [-80.74504, 35.4743], [-80.74071, 35.4754], [-80.73951, 35.47559], [-80.73847, 35.47566], [-80.73744, 35.47567], [-80.73381, 35.4753], [-80.73306, 35.47509], [-80.73093, 35.4743], [-80.73073, 35.47428], [-80.73073, 35.47428], [-80.73077, 35.47686], [-80.73074, 35.47717], [-80.73062, 35.47756], [-80.73002, 35.47847], [-80.72737, 35.48121], [-80.72737, 35.48121], [-80.72712, 35.48104], [-80.72674, 35.48088], [-80.72635, 35.48077], [-80.72578, 35.4807], [-80.72384, 35.48068], [-80.7201, 35.48076], [-80.71857, 35.48073], [-80.71793, 35.48077], [-80.71668, 35.48107], [-80.71242, 35.48242], [-80.70905, 35.48315], [-80.70905, 35.48315], [-80.70913, 35.48341], [-80.70894, 35.48408], [-80.70794, 35.48598], [-80.70774, 35.4866], [-80.70737, 35.48863], [-80.70687, 35.49044], [-80.70653, 35.49275], [-80.70589, 35.49429], [-80.70537, 35.49572], [-80.70446, 35.49884], [-80.70443, 35.49934], [-80.70455, 35.50021], [-80.70447, 35.50041], [-80.70434, 35.50056], [-80.70356, 35.50107], [-80.70289, 35.50136], [-80.69995, 35.50199], [-80.69934, 35.50197], [-80.69807, 35.5016], [-80.69723, 35.50145], [-80.69669, 35.50147], [-80.69601, 35.50156], [-80.69414, 35.50191], [-80.68939, 35.50302], [-80.68742, 35.50362], [-80.68501, 35.50429], [-80.68501, 35.50429], [-80.68404, 35.50083], [-80.6834, 35.49892], [-80.68315, 35.49846], [-80.68286, 35.49815], [-80.68243, 35.49797], [-80.68105, 35.49786], [-80.68022, 35.49774], [-80.67999, 35.49766], [-80.67965, 35.49748], [-80.67903, 35.49702], [-80.67858, 35.49657], [-80.67829, 35.49577], [-80.67802, 35.4946], [-80.67797, 35.494], [-80.67794, 35.49136], [-80.67789, 35.49096], [-80.67789, 35.49096], [-80.67733, 35.49105], [-80.67663, 35.49108], [-80.67561, 35.49098], [-80.67168, 35.49], [-80.67168, 35.49], [-80.67193, 35.48881], [-80.67378, 35.48357], [-80.67397, 35.48285], [-80.67408, 35.48202], [-80.67403, 35.48143], [-80.67306, 35.47712], [-80.67283, 35.47643], [-80.67129, 35.47322], [-80.67086, 35.47243], [-80.67069, 35.47194], [-80.67059, 35.47145], [-80.67042, 35.46914], [-80.67025, 35.46847], [-80.67002, 35.46791], [-80.66966, 35.46734], [-80.66882, 35.46621], [-80.66809, 35.46536], [-80.6676, 35.46466], [-80.66645, 35.46323], [-80.66569, 35.46239], [-80.66468, 35.4611], [-80.66421, 35.46035], [-80.66399, 35.45983], [-80.66387, 35.45938], [-80.66381, 35.45877], [-80.66384, 35.45828], [-80.66398, 35.45758], [-80.66446, 35.4566], [-80.66487, 35.45602], [-80.66487, 35.45602], [-80.66888, 35.45238], [-80.67001, 35.45124], [-80.67303, 35.44843], [-80.67385, 35.44772], [-80.67667, 35.44501], [-80.67812, 35.44324], [-80.67902, 35.44175], [-80.67947, 35.44081], [-80.68004, 35.4394], [-80.68025, 35.43848], [-80.68028, 35.43818], [-80.68024, 35.43811], [-80.68043, 35.43723], [-80.6805, 35.43642], [-80.68052, 35.43498], [-80.68034, 35.43314], [-80.67877, 35.42615], [-80.67857, 35.4255], [-80.67736, 35.42331], [-80.67736, 35.42318], [-80.67555, 35.42096], [-80.67481, 35.42028], [-80.6742, 35.41983], [-80.67388, 35.41964], [-80.67375, 35.41959], [-80.67364, 35.41961], [-80.67303, 35.41927], [-80.67303, 35.41927], [-80.67148, 35.41836], [-80.67027, 35.41757], [-80.66937, 35.41675], [-80.6681, 35.41544], [-80.66777, 35.41504], [-80.66624, 35.41238], [-80.66624, 35.41238], [-80.6692, 35.41117], [-80.67312, 35.4089], [-80.6735, 35.40858]]}, "date": "2012-03-30", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72939, 35.40268], [-80.72923, 35.40296], [-80.72912, 35.40332], [-80.72908, 35.40393], [-80.72956, 35.40612], [-80.72976, 35.40661], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75277, 35.42072], [-80.75314, 35.42107], [-80.75352, 35.42159], [-80.75726, 35.42794], [-80.75893, 35.42995], [-80.75928, 35.43016], [-80.75977, 35.43028], [-80.75977, 35.43028], [-80.76035, 35.43043], [-80.7609, 35.43069], [-80.7615, 35.43114], [-80.76222, 35.43187], [-80.76251, 35.43225], [-80.76261, 35.43246], [-80.76266, 35.43278], [-80.76265, 35.43332], [-80.76279, 35.43377], [-80.76308, 35.43413], [-80.76379, 35.43464], [-80.76412, 35.43503], [-80.76434, 35.43565], [-80.76441, 35.4362], [-80.76453, 35.43788], [-80.76452, 35.4382], [-80.76442, 35.43866], [-80.76442, 35.43866], [-80.76392, 35.43969], [-80.76382, 35.4401], [-80.76386, 35.44109], [-80.76401, 35.44217], [-80.76426, 35.44283], [-80.7658, 35.4448], [-80.76597, 35.44519], [-80.76602, 35.44555], [-80.76596, 35.44596], [-80.76568, 35.44642], [-80.76487, 35.44716], [-80.76418, 35.44801], [-80.76401, 35.44829], [-80.76397, 35.44856], [-80.76409, 35.44928], [-80.76409, 35.44955], [-80.76402, 35.45106], [-80.76393, 35.4515], [-80.76371, 35.45188], [-80.76308, 35.45271], [-80.76264, 35.45402], [-80.76246, 35.45445], [-80.76209, 35.4551], [-80.76173, 35.45558], [-80.76115, 35.45618], [-80.75998, 35.45718], [-80.75959, 35.45762], [-80.75939, 35.45793], [-80.75932, 35.45819], [-80.75922, 35.45922], [-80.75855, 35.46121], [-80.75779, 35.46275], [-80.75738, 35.46478], [-80.75633, 35.46824], [-80.75619, 35.46857], [-80.75016, 35.47668], [-80.75016, 35.47668], [-80.7483, 35.4754], [-80.74736, 35.47467], [-80.74646, 35.47374], [-80.74646, 35.47374], [-80.74571, 35.47408], [-80.74504, 35.4743], [-80.74071, 35.4754], [-80.73951, 35.47559], [-80.73847, 35.47566], [-80.73744, 35.47567], [-80.73381, 35.4753], [-80.73306, 35.47509], [-80.73093, 35.4743], [-80.73073, 35.47428], [-80.73073, 35.47428], [-80.73077, 35.47686], [-80.73074, 35.47717], [-80.73062, 35.47756], [-80.73002, 35.47847], [-80.72737, 35.48121], [-80.72737, 35.48121], [-80.72712, 35.48104], [-80.72674, 35.48088], [-80.72635, 35.48077], [-80.72578, 35.4807], [-80.72384, 35.48068], [-80.7201, 35.48076], [-80.71857, 35.48073], [-80.71793, 35.48077], [-80.71668, 35.48107], [-80.71242, 35.48242], [-80.70905, 35.48315], [-80.70905, 35.48315], [-80.70913, 35.48341], [-80.70894, 35.48408], [-80.70794, 35.48598], [-80.70774, 35.4866], [-80.70737, 35.48863], [-80.70687, 35.49044], [-80.70653, 35.49275], [-80.70589, 35.49429], [-80.70537, 35.49572], [-80.70446, 35.49884], [-80.70443, 35.49934], [-80.70455, 35.50021], [-80.70447, 35.50041], [-80.70434, 35.50056], [-80.70356, 35.50107], [-80.70289, 35.50136], [-80.69995, 35.50199], [-80.69934, 35.50197], [-80.69807, 35.5016], [-80.69723, 35.50145], [-80.69669, 35.50147], [-80.69601, 35.50156], [-80.69414, 35.50191], [-80.68939, 35.50302], [-80.68742, 35.50362], [-80.68501, 35.50429], [-80.68501, 35.50429], [-80.68404, 35.50083], [-80.6834, 35.49892], [-80.68315, 35.49846], [-80.68286, 35.49815], [-80.68243, 35.49797], [-80.68105, 35.49786], [-80.68022, 35.49774], [-80.67999, 35.49766], [-80.67965, 35.49748], [-80.67903, 35.49702], [-80.67858, 35.49657], [-80.67829, 35.49577], [-80.67802, 35.4946], [-80.67797, 35.494], [-80.67794, 35.49136], [-80.67789, 35.49096], [-80.67789, 35.49096], [-80.67733, 35.49105], [-80.67663, 35.49108], [-80.67561, 35.49098], [-80.67168, 35.49], [-80.67168, 35.49], [-80.67193, 35.48881], [-80.67378, 35.48357], [-80.67397, 35.48285], [-80.67408, 35.48202], [-80.67403, 35.48143], [-80.67306, 35.47712], [-80.67283, 35.47643], [-80.67129, 35.47322], [-80.67086, 35.47243], [-80.67069, 35.47194], [-80.67059, 35.47145], [-80.67042, 35.46914], [-80.67025, 35.46847], [-80.67002, 35.46791], [-80.66966, 35.46734], [-80.66882, 35.46621], [-80.66809, 35.46536], [-80.6676, 35.46466], [-80.66645, 35.46323], [-80.66569, 35.46239], [-80.66468, 35.4611], [-80.66421, 35.46035], [-80.66399, 35.45983], [-80.66387, 35.45938], [-80.66381, 35.45877], [-80.66384, 35.45828], [-80.66398, 35.45758], [-80.66446, 35.4566], [-80.66487, 35.45602], [-80.66487, 35.45602], [-80.66888, 35.45238], [-80.67001, 35.45124], [-80.67303, 35.44843], [-80.67385, 35.44772], [-80.67667, 35.44501], [-80.67812, 35.44324], [-80.67902, 35.44175], [-80.67947, 35.44081], [-80.68004, 35.4394], [-80.68025, 35.43848], [-80.68028, 35.43818], [-80.68024, 35.43811], [-80.68043, 35.43723], [-80.6805, 35.43642], [-80.68052, 35.43498], [-80.68034, 35.43314], [-80.67877, 35.42615], [-80.67857, 35.4255], [-80.67736, 35.42331], [-80.67736, 35.42318], [-80.67555, 35.42096], [-80.67481, 35.42028], [-80.6742, 35.41983], [-80.67388, 35.41964], [-80.67375, 35.41959], [-80.67364, 35.41961], [-80.67303, 35.41927], [-80.67303, 35.41927], [-80.67148, 35.41836], [-80.67027, 35.41757], [-80.66937, 35.41675], [-80.6681, 35.41544], [-80.66777, 35.41504], [-80.66624, 35.41238], [-80.66624, 35.41238], [-80.6692, 35.41117], [-80.67312, 35.4089], [-80.6735, 35.40858]]}, "date": "2012-03-31", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.91849, 35.51785], [-80.91832, 35.51756], [-80.91832, 35.51756], [-80.91902, 35.51734], [-80.91902, 35.51734], [-80.92117, 35.52427], [-80.92126, 35.52444], [-80.92189, 35.52498], [-80.92373, 35.52631], [-80.92506, 35.52712], [-80.92506, 35.52712], [-80.92376, 35.52832], [-80.92354, 35.5286], [-80.92336, 35.529], [-80.92292, 35.53117], [-80.92257, 35.53218], [-80.92209, 35.53317], [-80.92193, 35.53339], [-80.92116, 35.53423], [-80.9208, 35.53448], [-80.9208, 35.53448], [-80.92042, 35.53444], [-80.92013, 35.53459], [-80.92007, 35.53482], [-80.92018, 35.53507], [-80.91953, 35.53568], [-80.91937, 35.5359], [-80.91816, 35.53712], [-80.91768, 35.53774], [-80.91708, 35.53865], [-80.91699, 35.53889], [-80.91694, 35.53939], [-80.91725, 35.54242], [-80.9175, 35.54319], [-80.9177, 35.54356], [-80.91822, 35.54425], [-80.9186, 35.54469], [-80.92057, 35.54651], [-80.92143, 35.54737], [-80.92163, 35.54764], [-80.92326, 35.55075], [-80.92463, 35.55288], [-80.92475, 35.55321], [-80.92479, 35.5539], [-80.92458, 35.55452], [-80.9242, 35.55504], [-80.92265, 35.55629], [-80.92202, 35.5567], [-80.92114, 35.55753], [-80.92038, 35.55843], [-80.91896, 35.56039], [-80.91844, 35.56094], [-80.91766, 35.56161], [-80.91658, 35.56235], [-80.91541, 35.5629], [-80.91391, 35.56344], [-80.91227, 35.56396], [-80.91144, 35.5643], [-80.91072, 35.56466], [-80.90984, 35.56524], [-80.9092, 35.56579], [-80.90843, 35.56661], [-80.90762, 35.56762], [-80.90699, 35.56814], [-80.90631, 35.56851], [-80.90572, 35.56873], [-80.90504, 35.56889], [-80.90443, 35.56896], [-80.9038, 35.56899], [-80.90314, 35.56893], [-80.90149, 35.56849], [-80.9008, 35.56843], [-80.90011, 35.56846], [-80.89943, 35.56857], [-80.89909, 35.56865], [-80.89853, 35.56887], [-80.89787, 35.56937], [-80.89755, 35.56969], [-80.89601, 35.57213], [-80.89573, 35.57244], [-80.8954, 35.57268], [-80.89459, 35.57305], [-80.89162, 35.57395], [-80.89104, 35.57422], [-80.88943, 35.57518], [-80.88765, 35.57643], [-80.88599, 35.57814], [-80.88544, 35.57858], [-80.88469, 35.57909], [-80.88193, 35.58046], [-80.88131, 35.58073], [-80.87809, 35.58198], [-80.87615, 35.58254], [-80.87533, 35.58286], [-80.87486, 35.58314], [-80.8738, 35.58402], [-80.8738, 35.58402], [-80.87351, 35.58261], [-80.87341, 35.58235], [-80.87285, 35.58125], [-80.87278, 35.58119], [-80.87126, 35.57806], [-80.87025, 35.57617], [-80.86613, 35.56887], [-80.86601, 35.56849], [-80.86598, 35.5681], [-80.86614, 35.56612], [-80.86613, 35.56539], [-80.86542, 35.56164], [-80.86527, 35.56105], [-80.86512, 35.56071], [-80.86449, 35.55988], [-80.86308, 35.55826], [-80.8617, 35.55633], [-80.86117, 35.55572], [-80.86004, 35.55477], [-80.86001, 35.5547], [-80.85931, 35.55418], [-80.8574, 35.55315], [-80.8574, 35.55315], [-80.85679, 35.55276], [-80.85642, 35.5527], [-80.85612, 35.55271], [-80.85612, 35.55271], [-80.85596, 35.55264], [-80.85585, 35.55252], [-80.85586, 35.55229], [-80.85714, 35.55023], [-80.85784, 35.54894], [-80.85789, 35.54876], [-80.85787, 35.54834], [-80.85776, 35.54814], [-80.85739, 35.54785], [-80.85715, 35.54777], [-80.85689, 35.54774], [-80.85326, 35.54777], [-80.85093, 35.54774], [-80.84932, 35.54769], [-80.84809, 35.54759], [-80.84772, 35.54766], [-80.84682, 35.54808], [-80.84663, 35.5481], [-80.8461, 35.54802], [-80.8461, 35.54802], [-80.84655, 35.54634], [-80.8477, 35.54071], [-80.84789, 35.53926], [-80.84785, 35.53828], [-80.84777, 35.53781], [-80.84753, 35.5369], [-80.8386, 35.51319], [-80.83841, 35.51254], [-80.83836, 35.51177], [-80.83844, 35.51132], [-80.83878, 35.5105], [-80.83906, 35.51009], [-80.83961, 35.50944], [-80.8399, 35.50919], [-80.84038, 35.50888], [-80.84277, 35.50767], [-80.84334, 35.50722], [-80.84585, 35.50488], [-80.84621, 35.50434], [-80.84677, 35.50319], [-80.84848, 35.49926], [-80.84848, 35.49926], [-80.84537, 35.49862], [-80.84505, 35.49851], [-80.84426, 35.498], [-80.84174, 35.49611], [-80.84151, 35.49599], [-80.83693, 35.49456], [-80.83665, 35.49444], [-80.82061, 35.48452], [-80.81997, 35.48405], [-80.81997, 35.48405], [-80.81992, 35.4839], [-80.82006, 35.48347], [-80.81999, 35.47998], [-80.81993, 35.47907], [-80.81968, 35.47795], [-80.81968, 35.47795], [-80.82078, 35.47755], [-80.8237, 35.47559], [-80.82484, 35.47491], [-80.82484, 35.47491], [-80.82469, 35.47438], [-80.82487, 35.47399], [-80.82512, 35.4737], [-80.82599, 35.47322], [-80.82656, 35.47282], [-80.82699, 35.47259], [-80.82776, 35.47231], [-80.82807, 35.47226], [-80.82807, 35.47226], [-80.82827, 35.47184], [-80.82922, 35.47103], [-80.82941, 35.47076], [-80.82954, 35.47047], [-80.82957, 35.47024], [-80.82951, 35.46963], [-80.82951, 35.46963], [-80.82873, 35.46963], [-80.82841, 35.46957], [-80.82776, 35.46931], [-80.82473, 35.46763], [-80.82421, 35.46728], [-80.82295, 35.46631], [-80.822, 35.46556], [-80.82184, 35.46535], [-80.82161, 35.4649], [-80.82157, 35.46456], [-80.82161, 35.46418], [-80.82188, 35.46332], [-80.82188, 35.46307], [-80.82099, 35.46008], [-80.8208, 35.45901], [-80.82081, 35.45874], [-80.82114, 35.45733], [-80.82114, 35.45733], [-80.82191, 35.45736], [-80.82235, 35.45729], [-80.82292, 35.4571], [-80.82346, 35.45677], [-80.8248, 35.45557], [-80.82569, 35.45508], [-80.82569, 35.45508], [-80.82519, 35.45428], [-80.82478, 35.45344], [-80.82468, 35.4531], [-80.82448, 35.45284], [-80.82427, 35.45275], [-80.82243, 35.45225], [-80.82106, 35.45175], [-80.82032, 35.45139], [-80.81947, 35.45075], [-80.81947, 35.45075], [-80.81081, 35.45394], [-80.81071, 35.45393], [-80.80952, 35.45437], [-80.80952, 35.45437], [-80.80915, 35.45367], [-80.80883, 35.45325], [-80.80873, 35.45299], [-80.80874, 35.45266], [-80.80905, 35.45202], [-80.80909, 35.45169], [-80.80901, 35.45133], [-80.80835, 35.44964], [-80.8083, 35.44934], [-80.80843, 35.44883], [-80.8092, 35.44677], [-80.80935, 35.44614], [-80.80933, 35.44579], [-80.80882, 35.4434], [-80.80893, 35.44234], [-80.80915, 35.44185], [-80.80997, 35.44059], [-80.81098, 35.43885], [-80.81108, 35.43807], [-80.81127, 35.43767], [-80.81266, 35.43567], [-80.81266, 35.43567], [-80.81239, 35.43564], [-80.80952, 35.43566], [-80.80873, 35.43561], [-80.80834, 35.43552], [-80.80809, 35.43541], [-80.80764, 35.43509], [-80.80696, 35.43446], [-80.8058, 35.43394], [-80.80532, 35.43386], [-80.80441, 35.43388], [-80.80358, 35.43395], [-80.80358, 35.43395], [-80.80308, 35.43501], [-80.80302, 35.43527], [-80.80293, 35.43653], [-80.80293, 35.43653], [-80.80274, 35.43638], [-80.80167, 35.43512], [-80.7996, 35.43312], [-80.79428, 35.42758], [-80.79248, 35.42554], [-80.79248, 35.42554], [-80.79201, 35.42529], [-80.79105, 35.42488], [-80.78822, 35.42335], [-80.7879, 35.42324], [-80.7875, 35.42319], [-80.78718, 35.4232], [-80.78176, 35.42352], [-80.78116, 35.42347], [-80.77896, 35.42292], [-80.77821, 35.42269], [-80.77798, 35.4225], [-80.77754, 35.4219], [-80.77713, 35.42156], [-80.77666, 35.42129], [-80.77589, 35.42095], [-80.77551, 35.42084], [-80.77484, 35.42075], [-80.77335, 35.42061], [-80.77282, 35.42064], [-80.77201, 35.42084], [-80.76792, 35.42236], [-80.76747, 35.4226], [-80.76462, 35.42464], [-80.7644, 35.42474], [-80.764, 35.42478], [-80.7631, 35.42467], [-80.76016, 35.42421], [-80.75971, 35.42409], [-80.75839, 35.42362], [-80.75811, 35.42354], [-80.7578, 35.42352], [-80.75713, 35.42358], [-80.75628, 35.42376], [-80.75512, 35.42432], [-80.75512, 35.42432], [-80.7534, 35.42141], [-80.75294, 35.42086], [-80.75256, 35.42057], [-80.75208, 35.42031], [-80.75159, 35.42013], [-80.74601, 35.41862], [-80.74558, 35.41847], [-80.74309, 35.41645], [-80.74092, 35.4145], [-80.73984, 35.41359], [-80.73526, 35.41053], [-80.73379, 35.40906], [-80.73343, 35.40876], [-80.73307, 35.40855], [-80.73263, 35.40842], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73068, 35.40803], [-80.72976, 35.40661], [-80.72956, 35.40612], [-80.72908, 35.40393], [-80.72912, 35.40332], [-80.72923, 35.40296], [-80.72939, 35.40268]]}, "date": "2012-04-02", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-78.86458, 35.77229], [-78.86477, 35.77231], [-78.86508, 35.77242], [-78.86528, 35.77267], [-78.86534, 35.77284], [-78.86515, 35.77427], [-78.86518, 35.77466], [-78.86518, 35.77466], [-78.86468, 35.77476], [-78.86377, 35.77503], [-78.86301, 35.77503], [-78.86235, 35.77492], [-78.86197, 35.7749], [-78.86111, 35.77503], [-78.86066, 35.77501], [-78.86066, 35.77501], [-78.8603, 35.77647], [-78.85925, 35.77839], [-78.85908, 35.7797], [-78.85892, 35.7816], [-78.85857, 35.78253], [-78.858, 35.78368], [-78.85771, 35.784], [-78.85735, 35.7842], [-78.85676, 35.7843], [-78.85621, 35.78432], [-78.85621, 35.78432], [-78.85619, 35.78662], [-78.85621, 35.78693], [-78.8563, 35.78725], [-78.8565, 35.78761], [-78.85717, 35.78835], [-78.85741, 35.78853], [-78.85808, 35.78889], [-78.85823, 35.78916], [-78.85825, 35.7893], [-78.85825, 35.7893], [-78.859, 35.78915], [-78.86127, 35.78896], [-78.86666, 35.7888], [-78.86703, 35.78876], [-78.8676, 35.78865], [-78.86893, 35.78818], [-78.86977, 35.78798], [-78.87089, 35.7878], [-78.87089, 35.7878], [-78.87227, 35.78752], [-78.87393, 35.78728], [-78.8776, 35.78608], [-78.87933, 35.78524], [-78.8806, 35.78479], [-78.88142, 35.7846], [-78.88288, 35.78438], [-78.88417, 35.78398], [-78.88484, 35.78365], [-78.88691, 35.78221], [-78.88721, 35.78205], [-78.88749, 35.78196], [-78.89007, 35.78167], [-78.89207, 35.78121], [-78.89584, 35.78124], [-78.89912, 35.78121], [-78.8995, 35.78119], [-78.89991, 35.78108], [-78.89991, 35.78108], [-78.90001, 35.78116], [-78.90126, 35.78174], [-78.90172, 35.7819], [-78.90209, 35.78196], [-78.90369, 35.78203], [-78.90369, 35.78203], [-78.90366, 35.78617], [-78.9036, 35.78645], [-78.90336, 35.78696], [-78.90242, 35.78829], [-78.90208, 35.78893], [-78.90186, 35.78952], [-78.90052, 35.79486], [-78.89985, 35.79671], [-78.8997, 35.7973], [-78.89972, 35.79756], [-78.90027, 35.79892], [-78.90132, 35.801], [-78.90189, 35.8019], [-78.9025, 35.8025], [-78.9025, 35.8025], [-78.90249, 35.80257], [-78.90226, 35.80271], [-78.90183, 35.8028], [-78.90174, 35.80295], [-78.90236, 35.80682], [-78.90304, 35.80904], [-78.90331, 35.80972], [-78.90348, 35.81002], [-78.9035, 35.81041], [-78.9033, 35.81326], [-78.9024, 35.81723], [-78.90239, 35.82066], [-78.90223, 35.8223], [-78.90223, 35.8223], [-78.90241, 35.82225], [-78.90315, 35.82223], [-78.90722, 35.82252], [-78.909, 35.82322], [-78.90945, 35.82346], [-78.91059, 35.82446], [-78.9115, 35.82519], [-78.91192, 35.82551], [-78.91232, 35.82574], [-78.91283, 35.82594], [-78.91532, 35.8266], [-78.91564, 35.82689], [-78.91612, 35.82748], [-78.91612, 35.82748], [-78.91543, 35.82784], [-78.91519, 35.82804], [-78.91519, 35.82804], [-78.91564, 35.82833], [-78.91622, 35.82882], [-78.91664, 35.82898], [-78.91703, 35.82901], [-78.91854, 35.82869], [-78.91886, 35.82853], [-78.91937, 35.82849], [-78.92037, 35.8282], [-78.92062, 35.82807], [-78.92095, 35.82773], [-78.92095, 35.82773], [-78.92042, 35.82717], [-78.92028, 35.82685], [-78.92026, 35.82552], [-78.92026, 35.82552], [-78.91901, 35.82554], [-78.91871, 35.82561], [-78.91858, 35.82568], [-78.91782, 35.82643], [-78.91717, 35.82696], [-78.91668, 35.82717], [-78.91543, 35.82784], [-78.91511, 35.82811], [-78.91497, 35.82834], [-78.91458, 35.82948], [-78.91465, 35.82994], [-78.91512, 35.83084], [-78.91506, 35.83108], [-78.91494, 35.83127], [-78.91479, 35.83139], [-78.91397, 35.83172], [-78.91397, 35.83172], [-78.91503, 35.83354], [-78.91513, 35.83449], [-78.91508, 35.83473], [-78.91487, 35.83509], [-78.91463, 35.83528], [-78.91322, 35.83587], [-78.91276, 35.83625], [-78.91276, 35.83625], [-78.91199, 35.83593], [-78.91199, 35.83593], [-78.91103, 35.8377], [-78.91074, 35.83838], [-78.91063, 35.83884], [-78.91057, 35.8395], [-78.90967, 35.84299], [-78.90967, 35.84299], [-78.91058, 35.84306], [-78.91101, 35.84316], [-78.91167, 35.8434], [-78.9123, 35.84377], [-78.91283, 35.84422], [-78.91298, 35.84429], [-78.91323, 35.84454], [-78.91357, 35.84477], [-78.91387, 35.84494], [-78.9145, 35.84516], [-78.91495, 35.84524], [-78.91665, 35.84537], [-78.91849, 35.8458], [-78.91908, 35.8459], [-78.91966, 35.8459], [-78.92136, 35.84567], [-78.92187, 35.8457], [-78.92247, 35.84586], [-78.92451, 35.84681], [-78.9256, 35.84703], [-78.92615, 35.84706], [-78.92669, 35.84703], [-78.92723, 35.84689], [-78.92806, 35.8466], [-78.9284, 35.84655], [-78.92878, 35.84657], [-78.92914, 35.84666], [-78.93007, 35.84702], [-78.93051, 35.84724], [-78.93086, 35.8475], [-78.93207, 35.84882], [-78.93259, 35.84926], [-78.93309, 35.84953], [-78.93385, 35.84982], [-78.9345, 35.85014], [-78.9369, 35.85152], [-78.93731, 35.8518], [-78.93788, 35.85228], [-78.93856, 35.85305], [-78.93892, 35.8536], [-78.93917, 35.85409], [-78.94113, 35.85919], [-78.94133, 35.8598], [-78.94141, 35.86126], [-78.9416, 35.86185], [-78.94196, 35.86232], [-78.94308, 35.863], [-78.94352, 35.8634], [-78.94374, 35.86379], [-78.94412, 35.86507], [-78.94423, 35.86523], [-78.94423, 35.86523], [-78.94513, 35.8641], [-78.94595, 35.8632], [-78.94844, 35.86133], [-78.94881, 35.86099], [-78.94901, 35.86074], [-78.94935, 35.86016], [-78.95032, 35.85788], [-78.95078, 35.85704], [-78.95462, 35.85228], [-78.95627, 35.8497], [-78.95922, 35.84484], [-78.96218, 35.84124], [-78.96247, 35.8408], [-78.96304, 35.83969], [-78.96348, 35.83858], [-78.96362, 35.83774], [-78.9635, 35.83187], [-78.96351, 35.8309], [-78.96357, 35.83031], [-78.96387, 35.82912], [-78.96477, 35.82607], [-78.96494, 35.82503], [-78.96496, 35.82428], [-78.96475, 35.8206], [-78.96484, 35.81973], [-78.96512, 35.81862], [-78.96512, 35.81862], [-78.96047, 35.81851], [-78.95811, 35.81825], [-78.95757, 35.8183], [-78.95695, 35.81845], [-78.95627, 35.81878], [-78.9549, 35.81971], [-78.95135, 35.82139], [-78.95091, 35.82153], [-78.9488, 35.82198], [-78.9456, 35.82301], [-78.94525, 35.82319], [-78.94498, 35.82341], [-78.94405, 35.82469], [-78.94369, 35.82498], [-78.94342, 35.82511], [-78.94298, 35.82523], [-78.94253, 35.82525], [-78.94229, 35.82522], [-78.94229, 35.82522], [-78.94231, 35.82334], [-78.94222, 35.8214], [-78.94226, 35.82079], [-78.94247, 35.81949], [-78.943, 35.8169], [-78.9434, 35.81507], [-78.94365, 35.81421], [-78.94403, 35.8137], [-78.9442, 35.81357], [-78.94459, 35.81329], [-78.94682, 35.81195], [-78.94835, 35.81094], [-78.95097, 35.80882], [-78.95097, 35.80882], [-78.95081, 35.80862], [-78.9504, 35.80765], [-78.95019, 35.8073], [-78.94969, 35.80667], [-78.94923, 35.80635], [-78.94906, 35.80628], [-78.94808, 35.80608], [-78.94743, 35.80583], [-78.9471, 35.80564], [-78.94299, 35.80148], [-78.94247, 35.80087], [-78.942, 35.80003], [-78.94176, 35.79937], [-78.94017, 35.79431], [-78.94017, 35.79431], [-78.93954, 35.79435], [-78.93647, 35.79475], [-78.93547, 35.79479], [-78.93361, 35.79468], [-78.93095, 35.7944], [-78.93095, 35.7944], [-78.92961, 35.79409], [-78.92844, 35.79389], [-78.92588, 35.79361], [-78.92588, 35.79361], [-78.92612, 35.78966], [-78.92602, 35.78908], [-78.92584, 35.78875], [-78.92556, 35.78841], [-78.92409, 35.78717], [-78.92308, 35.78607], [-78.92276, 35.78541], [-78.92237, 35.78371], [-78.92219, 35.78308], [-78.92203, 35.78275], [-78.92182, 35.78252], [-78.9216, 35.78236], [-78.92041, 35.78185], [-78.91995, 35.78148], [-78.91974, 35.78115], [-78.91951, 35.78038], [-78.91932, 35.77925], [-78.91937, 35.77662], [-78.91926, 35.77515], [-78.91926, 35.77515], [-78.91709, 35.77589], [-78.91553, 35.77637], [-78.91326, 35.77695], [-78.90845, 35.77785], [-78.9074, 35.77801], [-78.90447, 35.77835], [-78.90405, 35.77845], [-78.9018, 35.77918], [-78.90132, 35.77938], [-78.90113, 35.77952], [-78.90096, 35.77973], [-78.90063, 35.7804], [-78.90033, 35.78082], [-78.90004, 35.78102], [-78.8996, 35.78117], [-78.89912, 35.78121], [-78.89584, 35.78124], [-78.89207, 35.78121], [-78.89007, 35.78167], [-78.88749, 35.78196], [-78.88721, 35.78205], [-78.88691, 35.78221], [-78.88484, 35.78365], [-78.88417, 35.78398], [-78.88288, 35.78438], [-78.88142, 35.7846], [-78.8806, 35.78479], [-78.87933, 35.78524], [-78.8776, 35.78608], [-78.87393, 35.78728], [-78.87147, 35.78766], [-78.87131, 35.78763], [-78.87071, 35.78769], [-78.87071, 35.78769], [-78.86933, 35.78796], [-78.86756, 35.78854], [-78.86702, 35.78864], [-78.8645, 35.78875], [-78.86127, 35.78883], [-78.85914, 35.789], [-78.85823, 35.78916], [-78.85823, 35.78916], [-78.85808, 35.78889], [-78.85741, 35.78853], [-78.85717, 35.78835], [-78.8565, 35.78761], [-78.8563, 35.78725], [-78.85621, 35.78693], [-78.85619, 35.78662], [-78.85621, 35.78432], [-78.85621, 35.78432], [-78.85676, 35.7843], [-78.85735, 35.7842], [-78.85771, 35.784], [-78.858, 35.78368], [-78.85857, 35.78253], [-78.85892, 35.7816], [-78.85908, 35.7797], [-78.85925, 35.77839], [-78.8603, 35.77647], [-78.86066, 35.77501], [-78.86066, 35.77501], [-78.86111, 35.77503], [-78.86197, 35.7749], [-78.86235, 35.77492], [-78.86301, 35.77503], [-78.86377, 35.77503], [-78.86468, 35.77476], [-78.86518, 35.77466], [-78.86518, 35.77466], [-78.86515, 35.77427], [-78.86534, 35.77302]]}, "date": "2012-04-04", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-78.86478, 35.77231], [-78.8646, 35.77229], [-78.86425, 35.77234], [-78.86367, 35.77256], [-78.86367, 35.77256], [-78.86385, 35.77294], [-78.86397, 35.77334], [-78.86396, 35.77424], [-78.8642, 35.77491], [-78.8642, 35.77491], [-78.86377, 35.77503], [-78.86301, 35.77503], [-78.86235, 35.77492], [-78.86197, 35.7749], [-78.86111, 35.77503], [-78.86066, 35.77501], [-78.86066, 35.77501], [-78.86096, 35.7736], [-78.86101, 35.77303], [-78.861, 35.77243], [-78.86082, 35.77153], [-78.86068, 35.77118], [-78.86047, 35.77088], [-78.85953, 35.77002], [-78.85922, 35.76963], [-78.85903, 35.76927], [-78.85897, 35.76905], [-78.85886, 35.76752], [-78.85886, 35.76752], [-78.86188, 35.76741], [-78.86265, 35.7673], [-78.863, 35.76721], [-78.86347, 35.76701], [-78.86557, 35.7658], [-78.86594, 35.76569], [-78.86651, 35.76565], [-78.86899, 35.76573], [-78.86938, 35.76572], [-78.86978, 35.76561], [-78.87045, 35.76524], [-78.87098, 35.76472], [-78.87188, 35.76324], [-78.87213, 35.76268], [-78.87242, 35.76227], [-78.87269, 35.76203], [-78.8731, 35.7618], [-78.87427, 35.76124], [-78.87479, 35.76076], [-78.87479, 35.76076], [-78.87525, 35.76098], [-78.8757, 35.76102], [-78.8757, 35.76102], [-78.87834, 35.76081], [-78.8796, 35.76067], [-78.88245, 35.76027], [-78.88274, 35.76018], [-78.88676, 35.75782], [-78.88834, 35.75677], [-78.88934, 35.75602], [-78.89035, 35.7555], [-78.89131, 35.75518], [-78.89226, 35.75499], [-78.89358, 35.75491], [-78.89449, 35.75498], [-78.8963, 35.7552], [-78.89732, 35.75524], [-78.89792, 35.7552], [-78.89835, 35.75512], [-78.89958, 35.7548], [-78.90031, 35.75448], [-78.90223, 35.75345], [-78.90263, 35.75328], [-78.903, 35.75304], [-78.90513, 35.75084], [-78.90563, 35.7505], [-78.90645, 35.75015], [-78.90955, 35.74952], [-78.90955, 35.74952], [-78.91068, 35.75144], [-78.9109, 35.7517], [-78.91117, 35.75192], [-78.91153, 35.75215], [-78.91259, 35.75267], [-78.91279, 35.75273], [-78.9138, 35.75325], [-78.91637, 35.75481], [-78.91677, 35.75512], [-78.91709, 35.75548], [-78.91785, 35.75662], [-78.9192, 35.75909], [-78.9195, 35.75954], [-78.91972, 35.75976], [-78.92115, 35.76082], [-78.92251, 35.76206], [-78.92316, 35.76273], [-78.92398, 35.76398], [-78.92467, 35.76538], [-78.92641, 35.76699], [-78.92676, 35.76745], [-78.92712, 35.76814], [-78.92721, 35.76844], [-78.92721, 35.76867], [-78.92711, 35.76902], [-78.92715, 35.76915], [-78.9271, 35.76954], [-78.92709, 35.77055], [-78.92726, 35.77153], [-78.92729, 35.77248], [-78.92735, 35.77264], [-78.92735, 35.77264], [-78.92661, 35.77287], [-78.92497, 35.77322], [-78.91709, 35.77589], [-78.91553, 35.77637], [-78.91326, 35.77695], [-78.90845, 35.77785], [-78.9074, 35.77801], [-78.90447, 35.77835], [-78.90405, 35.77845], [-78.9018, 35.77918], [-78.90132, 35.77938], [-78.90113, 35.77952], [-78.90096, 35.77973], [-78.90063, 35.7804], [-78.90033, 35.78082], [-78.90004, 35.78102], [-78.8996, 35.78117], [-78.89912, 35.78121], [-78.89584, 35.78124], [-78.89207, 35.78121], [-78.89007, 35.78167], [-78.88749, 35.78196], [-78.88721, 35.78205], [-78.88691, 35.78221], [-78.88484, 35.78365], [-78.88417, 35.78398], [-78.88288, 35.78438], [-78.88142, 35.7846], [-78.8806, 35.78479], [-78.87933, 35.78524], [-78.8776, 35.78608], [-78.87393, 35.78728], [-78.87147, 35.78766], [-78.87131, 35.78763], [-78.8709, 35.78767], [-78.8709, 35.78767], [-78.87117, 35.78573], [-78.87117, 35.78573], [-78.87045, 35.78569], [-78.87015, 35.78563], [-78.86978, 35.78544], [-78.86966, 35.78533], [-78.86957, 35.78499], [-78.86963, 35.78484], [-78.86994, 35.78442], [-78.8699, 35.78419], [-78.8699, 35.78419], [-78.86931, 35.78439], [-78.86855, 35.78477], [-78.86828, 35.78477], [-78.8681, 35.78472], [-78.86786, 35.78453], [-78.86781, 35.78433], [-78.86787, 35.78412], [-78.86799, 35.78396], [-78.86923, 35.78352], [-78.86947, 35.78336], [-78.86987, 35.78293], [-78.86999, 35.78271], [-78.87003, 35.78242], [-78.87005, 35.78158], [-78.87039, 35.78063], [-78.87033, 35.78022], [-78.87021, 35.78], [-78.86956, 35.77931], [-78.86905, 35.77905], [-78.86808, 35.77869], [-78.86765, 35.77843], [-78.86751, 35.77823], [-78.86748, 35.77806], [-78.8676, 35.77773], [-78.8676, 35.77773], [-78.86717, 35.77746], [-78.86717, 35.77746], [-78.86727, 35.7772], [-78.86728, 35.77657], [-78.86715, 35.77631], [-78.86682, 35.77604], [-78.86642, 35.77558], [-78.86631, 35.77539], [-78.86625, 35.77517], [-78.86627, 35.77465], [-78.86627, 35.77465], [-78.86518, 35.77466], [-78.86518, 35.77466], [-78.86515, 35.77427], [-78.86534, 35.77284], [-78.86528, 35.77267], [-78.86508, 35.77242], [-78.86487, 35.77234]]}, "date": "2012-04-07", "type": "Feature"}, {"geometry": {"type": "LineString", "coordinates": [[-80.72939, 35.40268], [-80.72923, 35.40296], [-80.72912, 35.40332], [-80.72908, 35.40393], [-80.72956, 35.40612], [-80.72976, 35.40661], [-80.73068, 35.40803], [-80.73078, 35.40827], [-80.73078, 35.40827], [-80.73263, 35.40842], [-80.73307, 35.40855], [-80.73343, 35.40876], [-80.73379, 35.40906], [-80.73526, 35.41053], [-80.73984, 35.41359], [-80.74092, 35.4145], [-80.74309, 35.41645], [-80.74558, 35.41847], [-80.74601, 35.41862], [-80.75159, 35.42013], [-80.75208, 35.42031], [-80.75277, 35.42072], [-80.75314, 35.42107], [-80.75352, 35.42159], [-80.75726, 35.42794], [-80.75893, 35.42995], [-80.75928, 35.43016], [-80.75977, 35.43028], [-80.75977, 35.43028], [-80.76035, 35.43043], [-80.7609, 35.43069], [-80.7615, 35.43114], [-80.76222, 35.43187], [-80.76251, 35.43225], [-80.76261, 35.43246], [-80.76266, 35.43278], [-80.76265, 35.43332], [-80.76279, 35.43377], [-80.76308, 35.43413], [-80.76379, 35.43464], [-80.76412, 35.43503], [-80.76434, 35.43565], [-80.76441, 35.4362], [-80.76453, 35.43788], [-80.76452, 35.4382], [-80.76442, 35.43866], [-80.76442, 35.43866], [-80.76392, 35.43969], [-80.76382, 35.4401], [-80.76386, 35.44109], [-80.76401, 35.44217], [-80.76426, 35.44283], [-80.7658, 35.4448], [-80.76597, 35.44519], [-80.76602, 35.44555], [-80.76596, 35.44596], [-80.76568, 35.44642], [-80.76487, 35.44716], [-80.76418, 35.44801], [-80.76401, 35.44829], [-80.76397, 35.44856], [-80.76409, 35.44928], [-80.76409, 35.44955], [-80.76402, 35.45106], [-80.76393, 35.4515], [-80.76371, 35.45188], [-80.76308, 35.45271], [-80.76264, 35.45402], [-80.76246, 35.45445], [-80.76209, 35.4551], [-80.76173, 35.45558], [-80.76115, 35.45618], [-80.75998, 35.45718], [-80.75959, 35.45762], [-80.75939, 35.45793], [-80.75932, 35.45819], [-80.75922, 35.45922], [-80.75855, 35.46121], [-80.75779, 35.46275], [-80.75738, 35.46478], [-80.75633, 35.46824], [-80.75619, 35.46857], [-80.75016, 35.47668], [-80.75016, 35.47668], [-80.75134, 35.47748], [-80.75278, 35.47857], [-80.75522, 35.48055], [-80.75605, 35.48133], [-80.75679, 35.48229], [-80.75731, 35.48316], [-80.76001, 35.48856], [-80.76001, 35.48856], [-80.76471, 35.49024], [-80.76471, 35.49024], [-80.76692, 35.48734], [-80.77014, 35.48202], [-80.77029, 35.48141], [-80.77043, 35.47728], [-80.77051, 35.47695], [-80.77063, 35.47675], [-80.77129, 35.47627], [-80.77338, 35.47499], [-80.77385, 35.47479], [-80.77466, 35.47456], [-80.77496, 35.47451], [-80.77555, 35.47452], [-80.78021, 35.47508], [-80.78021, 35.47508], [-80.78108, 35.47516], [-80.7814, 35.47515], [-80.78169, 35.47508], [-80.78513, 35.47345], [-80.78513, 35.47345], [-80.78519, 35.47351], [-80.7864, 35.47851], [-80.78655, 35.47896], [-80.78812, 35.48223], [-80.7884, 35.48261], [-80.78921, 35.48339], [-80.78938, 35.48364], [-80.78949, 35.48391], [-80.78954, 35.48437], [-80.78947, 35.48534], [-80.78953, 35.4857], [-80.78968, 35.48607], [-80.79059, 35.48729], [-80.79077, 35.48762], [-80.79088, 35.48799], [-80.7909, 35.48831], [-80.79088, 35.48957], [-80.79096, 35.48994], [-80.79114, 35.49034], [-80.79134, 35.49062], [-80.7917, 35.49097], [-80.7953, 35.49395], [-80.79562, 35.4943], [-80.79582, 35.4946], [-80.79609, 35.49521], [-80.79621, 35.49579], [-80.7962, 35.49629], [-80.79592, 35.49782], [-80.79587, 35.49837], [-80.79602, 35.50063], [-80.79599, 35.5009], [-80.79449, 35.50359], [-80.79435, 35.50407], [-80.79431, 35.50461], [-80.79454, 35.50565], [-80.79477, 35.50604], [-80.79503, 35.50628], [-80.79549, 35.50658], [-80.79666, 35.50716], [-80.79666, 35.50716], [-80.79725, 35.50738], [-80.79769, 35.5078], [-80.79829, 35.50874], [-80.79861, 35.50931], [-80.79867, 35.5095], [-80.79874, 35.50979], [-80.79878, 35.51058], [-80.79874, 35.51356], [-80.79877, 35.51507], [-80.79935, 35.51739], [-80.79929, 35.5179], [-80.799, 35.5188], [-80.79879, 35.51977], [-80.79866, 35.52057], [-80.79863, 35.5211], [-80.79874, 35.52158], [-80.7994, 35.52291], [-80.79947, 35.52326], [-80.79942, 35.5237], [-80.79915, 35.52421], [-80.79729, 35.52663], [-80.79716, 35.52693], [-80.79719, 35.52724], [-80.79829, 35.52956], [-80.7995, 35.5326], [-80.79983, 35.53326], [-80.80225, 35.53698], [-80.80457, 35.53976], [-80.80494, 35.54027], [-80.80513, 35.54095], [-80.80551, 35.54362], [-80.80582, 35.54477], [-80.80634, 35.54573], [-80.80817, 35.54838], [-80.80955, 35.55208], [-80.81046, 35.55502], [-80.81067, 35.55536], [-80.81132, 35.55612], [-80.81156, 35.55655], [-80.81249, 35.55928], [-80.81396, 35.56333], [-80.81468, 35.56476], [-80.81597, 35.56692], [-80.8168, 35.5688], [-80.81707, 35.56952], [-80.81707, 35.56952], [-80.81784, 35.57139], [-80.81799, 35.57249], [-80.81806, 35.57268], [-80.81822, 35.57294], [-80.81879, 35.57421], [-80.81888, 35.57425], [-80.81888, 35.57425], [-80.81921, 35.5742], [-80.82115, 35.57283], [-80.82115, 35.57283], [-80.82285, 35.57432], [-80.82285, 35.57432], [-80.81815, 35.57825], [-80.81815, 35.57825], [-80.81952, 35.57942], [-80.8209, 35.58055], [-80.8211, 35.58067], [-80.8213, 35.58074], [-80.82422, 35.58122], [-80.8275, 35.58121], [-80.82801, 35.58115], [-80.82841, 35.58103], [-80.82968, 35.58032], [-80.82987, 35.58026], [-80.8303, 35.58026], [-80.8309, 35.58037], [-80.83136, 35.58035], [-80.83236, 35.5801], [-80.83443, 35.57967], [-80.83733, 35.57974], [-80.83816, 35.57956], [-80.83962, 35.57906], [-80.84025, 35.57906], [-80.84025, 35.57906], [-80.84154, 35.57907], [-80.84187, 35.57901], [-80.84249, 35.57878], [-80.84303, 35.57836], [-80.84359, 35.57759], [-80.84378, 35.57741], [-80.84417, 35.57716], [-80.84447, 35.57704], [-80.84495, 35.57694], [-80.84639, 35.57685], [-80.84674, 35.57688], [-80.84716, 35.57696], [-80.84775, 35.57723], [-80.84841, 35.57763], [-80.84929, 35.57828], [-80.84989, 35.57858], [-80.8531, 35.57947], [-80.85407, 35.57978], [-80.85518, 35.57985], [-80.85953, 35.57979], [-80.8603, 35.5799], [-80.8609, 35.58007], [-80.86129, 35.58022], [-80.86159, 35.58039], [-80.86168, 35.58052], [-80.86373, 35.58233], [-80.8645, 35.58291], [-80.86673, 35.58403], [-80.86765, 35.58443], [-80.86807, 35.58456], [-80.86869, 35.58468], [-80.86959, 35.58474], [-80.87014, 35.58471], [-80.87311, 35.58437], [-80.87382, 35.5841], [-80.87503, 35.58314], [-80.87539, 35.58293], [-80.87596, 35.5827], [-80.87813, 35.58206], [-80.88136, 35.58081], [-80.88218, 35.58044], [-80.88441, 35.57932], [-80.88474, 35.57914], [-80.88605, 35.57817], [-80.88751, 35.57667], [-80.8881, 35.5762], [-80.88894, 35.5756], [-80.89069, 35.57454], [-80.89166, 35.57404], [-80.89369, 35.57345], [-80.89496, 35.57303], [-80.89554, 35.57274], [-80.8958, 35.57256], [-80.89615, 35.57217], [-80.89744, 35.57011], [-80.89781, 35.56961], [-80.89845, 35.5691], [-80.89894, 35.56886], [-80.89945, 35.56868], [-80.89991, 35.56858], [-80.90085, 35.56854], [-80.90128, 35.56858], [-80.90302, 35.56902], [-80.90389, 35.56911], [-80.90427, 35.5691], [-80.90485, 35.56905], [-80.90555, 35.56891], [-80.90636, 35.56863], [-80.9068, 35.5684], [-80.9074, 35.56799], [-80.90793, 35.56751], [-80.90884, 35.56639], [-80.90942, 35.56578], [-80.90999, 35.56531], [-80.91072, 35.5648], [-80.91133, 35.56447], [-80.91168, 35.56433], [-80.91396, 35.56357], [-80.916, 35.56277], [-80.91696, 35.56227], [-80.91774, 35.56174], [-80.91867, 35.56089], [-80.91923, 35.56028], [-80.92049, 35.5585], [-80.9211, 35.55774], [-80.92151, 35.55732], [-80.92258, 35.5564], [-80.92265, 35.55629], [-80.9242, 35.55504], [-80.92458, 35.55452], [-80.92471, 35.55423], [-80.92479, 35.5539], [-80.9248, 35.55355], [-80.92475, 35.55321], [-80.92463, 35.55288], [-80.92326, 35.55075], [-80.92163, 35.54764], [-80.92143, 35.54737], [-80.92057, 35.54651], [-80.9186, 35.54469], [-80.91822, 35.54425], [-80.9177, 35.54356], [-80.9175, 35.54319], [-80.91725, 35.54242], [-80.91694, 35.53939], [-80.91699, 35.53889], [-80.91708, 35.53865], [-80.91768, 35.53774], [-80.9185, 35.53674], [-80.91929, 35.53597], [-80.91962, 35.53574], [-80.92027, 35.53513], [-80.92027, 35.53513], [-80.92046, 35.53518], [-80.92066, 35.53517], [-80.92079, 35.53513], [-80.92092, 35.53502], [-80.92099, 35.53482], [-80.92088, 35.53455], [-80.92102, 35.53435], [-80.92171, 35.53367], [-80.92209, 35.53317], [-80.92267, 35.53196], [-80.92284, 35.53146], [-80.92342, 35.52883], [-80.92376, 35.52832], [-80.92506, 35.52712], [-80.92506, 35.52712], [-80.92373, 35.52631], [-80.92189, 35.52498], [-80.92126, 35.52444], [-80.92117, 35.52427], [-80.91902, 35.51734], [-80.91902, 35.51734], [-80.91832, 35.51756], [-80.91832, 35.51756], [-80.91849, 35.51785]]}, "date": "2012-04-09", "type": "Feature"}]} POINT (10 12) xn76urx4epb0 Earthquakes International earthquake observation labs 2005-12-13T18:30:02Z Dr. Thaddeus Remor tremor@quakelab.edu urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 This has multiple georss entries in it urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a 2005-08-17T07:02:32Z We just had a big one. 45.256 -71.92 42.256 -69.92 45.256 -110.45 46.46 -109.48 43.84 -109.86 45.256 -110.45 46.46 -109.48 43.84 -109.86 45.256 -110.45 Another Entryd Another point. 46.256 -70.92 POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10)) Paths Examples of paths. Note that the tessellate tag is by default set to 0. If you want to create tessellated lines, they must be authored (or edited) directly in KML. Absolute Extruded Transparent green wall with yellow outlines #yellowLineGreenPoly 1 1 absolute -112.2550785337791,36.07954952145647,2357 -112.2549277039738,36.08117083492122,2357 -112.2552505069063,36.08260761307279,2357 -112.2564540158376,36.08395660588506,2357 -112.2580238976449,36.08511401044813,2357 -112.2595218489022,36.08584355239394,2357 -112.2608216347552,36.08612634548589,2357 -112.262073428656,36.08626019085147,2357 -112.2633204928495,36.08621519860091,2357 -112.2644963846444,36.08627897945274,2357 -112.2656969554589,36.08649599090644,2357 geometry = $this->createLineString(50); } /** * @BeforeMethods("setUpLineString") * @Revs(100) */ public function benchInvertXY(): void { $this->geometry->invertXY(); } /** * @BeforeMethods("setUpLineString") * @Revs(200) */ public function benchIsEmpty(): void { $this->geometry->isEmpty(); } /** * @BeforeMethods("setUpLineString") */ public function benchIsSimple(): void { $this->geometry->isSimple(); } /** * @BeforeMethods("setUpLineString") */ public function benchAsArray(): void { $this->geometry->asArray(); } /** * @BeforeMethods("setUpLineString") */ public function benchGetBBox(): void { $this->geometry->getBBox(); } /** * @BeforeMethods("setUpLineString") */ public function benchExplode(): void { $this->geometry->explode(); } /** * @BeforeMethods("setUpLineString") */ public function benchExplodeTrue(): void { $this->geometry->explode(true); } /** * @BeforeMethods("setUpLineString") * @Revs(200) */ public function benchGeometryN(): void { $this->geometry->geometryN(10); } /** * @BeforeMethods("setUpLineString") * @Revs(200) */ public function benchEndPoint(): void { $this->geometry->endPoint(); } /** * @BeforeMethods("setUpLineString") */ public function benchLength(): void { $this->geometry->length(); } /** * @BeforeMethods("setUpLineString") */ public function benchLength3D(): void { $this->geometry->length3D(); } /** * @BeforeMethods("setUpLineString") */ public function benchGreatCircleLength(): void { $this->geometry->greatCircleLength(); } /** * @BeforeMethods("setUpLineString") */ public function benchHaversineLength(): void { $this->geometry->haversineLength(); } /** * @BeforeMethods("setUpLineString") */ public function benchVincentyLength(): void { $this->geometry->vincentyLength(); } /** * @BeforeMethods("setUpLineString") */ public function benchIsClosed(): void { $this->geometry->isClosed(); } } */ protected function createPolygonComponents(int $pointCount = 4, int $rings = 1): array { $components = []; for ($i = 0; $i < $rings; ++$i) { $components[] = $this->createLineString($pointCount, true); } return $components; } protected function createPolygon(int $pointCount = 4, int $rings = 1): Polygon { return new Polygon($this->createPolygonComponents($pointCount, $rings)); } protected function createMultiPolygon(int $pointCount = 4, int $rings = 1, int $polygons = 1): MultiPolygon { $components = []; for ($i = 0; $i < $polygons; ++$i) { $components[] = $this->createPolygon($pointCount, $rings); } return new MultiPolygon($components); } protected function createGeometryCollection(int $scale = 1): GeometryCollection { $gc1 = new GeometryCollection(array_fill(0, 1 * $scale, new Point(1, 2))); $gc2 = new GeometryCollection(array_fill(0, 1 * $scale, $gc1)); $gc3 = new GeometryCollection(array_fill(0, 1 * $scale, $gc2)); return new GeometryCollection( array_merge( array_fill(0, 10 * $scale, new Point(1, 2)), array_fill(0, 10 * $scale, $this->createLineString(10 * $scale)), array_fill(0, 10 * $scale, $this->createPolygon(2 * $scale + 4, 5)), array_fill(0, 1 * $scale, $this->createMultiPolygon($scale + 4, 5, 5)), array_fill(0, 1 * $scale, $gc3) ) ); } } geometry = $this->createGeometryCollection(1); } public function setUpGeometryCollectionSmall(): void { $this->geometry = $this->createGeometryCollection(1); } /** * @BeforeMethods("setUpGeometryCollectionBig") */ public function benchGetPoints(): void { $this->geometry->getPoints(); } /** * @BeforeMethods("setUpGeometryCollectionBig") */ public function benchExplodeGeometries(): void { $this->geometry->explodeGeometries(); } } geometry = $this->createPolygon(100, 10); } public function setUpPolygonLarge(): void { $this->geometry = $this->createPolygon(1000, 100); } /** * @return array */ public function providePolygonComponents(): array { return [[$this->createPolygonComponents(10, 10)]]; } /** * @ParamProviders("providePolygonComponents") * * @param array $params */ public function benchCreatePolygon(array $params): void { new Polygon($params[0]); } /** * @BeforeMethods("setUpPolygonLarge") * @Revs(200) */ public function benchIsEmpty(): void { ($this->geometry->isEmpty()); } /** * @BeforeMethods("setUpPolygonLarge") * @Revs(200) */ public function benchExteriorRing(): void { $this->geometry->exteriorRing(); } /** * @BeforeMethods("setUpPolygonSmall") * @Revs(10) */ public function benchGetPoints(): void { $this->geometry->getPoints(); } /** * @BeforeMethods("setUpPolygonSmall") * @Revs(10) */ public function benchArea(): void { $this->geometry->area(); } } SNJkF𔰈i)JܱB }9GBMB