├── README.mdown ├── geophp ├── LICENSE ├── README.md ├── geoPHP.inc ├── lib │ ├── adapters │ │ ├── GPX.class.php │ │ ├── GeoAdapter.class.php │ │ ├── GeoJSON.class.php │ │ ├── GeoRSS.class.php │ │ ├── GoogleGeocode.class.php │ │ ├── KML.class.php │ │ ├── WKB.class.php │ │ └── WKT.class.php │ └── geometry │ │ ├── Collection.class.php │ │ ├── Geometry.class.php │ │ ├── GeometryCollection.class.php │ │ ├── LineString.class.php │ │ ├── MultiLineString.class.php │ │ ├── MultiPoint.class.php │ │ ├── MultiPolygon.class.php │ │ ├── Point.class.php │ │ └── Polygon.class.php └── tests │ ├── input │ ├── barret_spur.gpx │ ├── big_n_ugly.kml │ ├── box.georss │ ├── cdata.kml │ ├── circle.georss │ ├── fells_loop.gpx │ ├── geometrycollection.georss │ ├── geometrycollection.wkt │ ├── line.georss │ ├── linestring.wkt │ ├── multilinestring.wkt │ ├── multipolygon.wkb │ ├── multipolygon.wkt │ ├── path.kml │ ├── pentagon.kml │ ├── point.georss │ ├── point.kml │ ├── point.wkt │ ├── polygon.georss │ ├── polygon.wkt │ ├── polygon2.wkt │ ├── polygon3.wkt │ └── track.gpx │ └── test.php └── shpParser.php /README.mdown: -------------------------------------------------------------------------------- 1 | #Shapefile Parser 2 | 3 | This class allows for the extraction of data from a ESRI shapefile. The class is early in development and thus likely to be in flux. 4 | 5 | ## Functions 6 | 7 | - *load* - Takes a .shp file path, loads data into class. 8 | - *headerInfo* - Returns header info from loaded .shp file. 9 | - *getShapeData* - Returns an array of geo data from .shp data in WKT format. 10 | 11 | ## Goals 12 | 13 | - Read both plain .shp files and archives with associated .shp data. 14 | - When reading an archive, also pull in meta information from data. 15 | - Lower memory footprint. 16 | - Better documentation. 17 | -------------------------------------------------------------------------------- /geophp/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009, Camptocamp 2 | Copyright (c) 2011, Patrick Hayes 3 | Copyright (c) 2010, GeoMemes Research 4 | Copyright (c) 2010-2011, Arnaud Renevier 5 | 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | 11 | * Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | * Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in the 15 | documentation and/or other materials provided with the distribution. 16 | * Neither the name of the University of California, Berkeley nor the 17 | names of its contributors may be used to endorse or promote products 18 | derived from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY 21 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 24 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /geophp/README.md: -------------------------------------------------------------------------------- 1 | GeoPHP is a open-source native PHP library for doing geometry operations. It is written entirely in PHP and 2 | can therefore run on shared hosts. It can read and write a wide variety of formats (WKT, WKB, GeoJSON, 3 | KML, GPX, GeoRSS). It works with all Simple-Feature geometries (Point, LineString, Polygon, GeometryCollection etc.) 4 | and can be used to get centroids, bounding-boxes, area, and a wide variety of other useful information. 5 | 6 | geoPHP also helpfully wraps the GEOS php extension so that applications can get a transparent performance 7 | increase when GEOS is installed on the server. When GEOS is installed, geoPHP also becomes 8 | fully compliant with the OpenGIS® Implementation Standard for Geographic information. This means that 9 | you get the full-set of openGIS functions in PHP like Union, IsWithin, Touches etc. 10 | 11 | Applications get a useful "core-set" of geometry operations that work in all environments, and an "extended-set" 12 | of operations for environments that have GEOS installed. As time and resources allow we will be porting as much as 13 | possible to native PHP to enable more operations on hosts without GEOS. 14 | 15 | See the 'getting started' section below for references and examples of everything that geoPHP can do. 16 | 17 | This project is currently looking for co-maintainers. If you think you can help out, please send me a 18 | message. Forks are also welcome, please issue pull requests and I will merge them into the main branch. 19 | 20 | Getting Started 21 | ----------------------- 22 | 23 | * The lastest stable version can always be downloaded at: 24 | * Read the API Reference at: 25 | * Examples 26 | * Using geoPHP as a GIS format converter: 27 | * Other Interesting Links: 28 | * Learn about GEOS integration at: 29 | 30 | Example usage 31 | ------------------------------------------------- 32 | 33 | include_once('geoPHP.inc'); 34 | 35 | // Polygon WKT example 36 | $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'); 37 | $area = $polygon->getArea(); 38 | $centroid = $polygon->getCentroid(); 39 | $centX = $centroid->getX(); 40 | $centY = $centroid->getY(); 41 | 42 | print "This polygon has an area of ".$area." and a centroid with X=".$centX." and Y=".$centY; 43 | 44 | // MultiPoint json example 45 | print "
"; 46 | $json = 47 | '{ 48 | "type": "MultiPoint", 49 | "coordinates": [ 50 | [100.0, 0.0], [101.0, 1.0] 51 | ] 52 | }'; 53 | 54 | $multipoint = geoPHP::load($json, 'json'); 55 | $multipoint_points = $multipoint->getComponents(); 56 | $first_wkt = $multipoint_points[0]->out('wkt'); 57 | 58 | print "This multipolygon has ".$multipoint->numGeometries()." points. The first point has a wkt representation of ".$first_wkt; 59 | 60 | 61 | Credit 62 | ------------------------------------------------- 63 | 64 | Maintainer: Patrick Hayes 65 | 66 | Code From: 67 | 68 | * sfMapFish Plugin by camptocamp 69 | * CIS by GeoMemes Research 70 | * gisconverter.php by Arnaud Renevier 71 | * Dave Tarc 72 | 73 | Where code from other projects or authors is included, those authors are included in the copyright notice in that file 74 | -------------------------------------------------------------------------------- /geophp/geoPHP.inc: -------------------------------------------------------------------------------- 1 | 'WKT', 70 | 'wkb' => 'WKB', 71 | 'json' => 'GeoJSON', 72 | 'kml' => 'KML', 73 | 'gpx' => 'GPX', 74 | 'georss' => 'GeoRSS', 75 | 'google_geocode' => 'GoogleGeocode', 76 | ); 77 | } 78 | 79 | static function geometryList() { 80 | return array( 81 | 'point' => 'Point', 82 | 'linestring' => 'LineString', 83 | 'polygon' => 'Polygon', 84 | 'multipoint' => 'MultiPoint', 85 | 'multilinestring' => 'MultiLineString', 86 | 'multipolygon' => 'MultiPolygon', 87 | 'geometrycollection' => 'GeometryCollection', 88 | ); 89 | } 90 | 91 | static function geosInstalled($force = NULL) { 92 | static $geos_installed = NULL; 93 | if ($force !== NULL) $geos_installed = $force; 94 | if ($geos_installed !== NULL) { 95 | return $geos_installed; 96 | } 97 | $geos_installed = class_exists('GEOSGeometry'); 98 | return $geos_installed; 99 | } 100 | 101 | static function geosToGeometry($geos) { 102 | if (!geoPHP::geosInstalled()) { 103 | return NULL; 104 | } 105 | $wkb_writer = new GEOSWKBWriter(); 106 | $wkb = $wkb_writer->writeHEX($geos); 107 | $geometry = geoPHP::load($wkb,'wkb',TRUE); 108 | //@@TODO: We no longer need to check this once we have Empty geometries 109 | if ($geometry) { 110 | $geometry->setGeos($geos); 111 | return $geometry; 112 | } 113 | } 114 | 115 | // Reduce a geometry, or an array of geometries, into their 'lowest' available common geometry. 116 | // For example a GeometryCollection of only points will become a MultiPoint 117 | // A multi-point containing a single point will return a point. 118 | // An array of geometries can be passed and they will be compiled into a single geometry 119 | static function geometryReduce($geometry) { 120 | // If it's an array of one, then just parse the one 121 | if (is_array($geometry)) { 122 | if (count($geometry) == 1) return geoPHP::geometryReduce($geometry[0]); 123 | } 124 | 125 | // If the geometry cannot even theoretically be reduced more, then pass it back 126 | if (gettype($geometry) == 'object') { 127 | $passbacks = array('Point','LineString','Polygon'); 128 | if (in_array($geometry->geometryType(),$passbacks)) { 129 | return $geometry; 130 | } 131 | } 132 | 133 | // If it is a mutlti-geometry, check to see if it just has one member 134 | // If it does, then pass the member, if not, then just pass back the geometry 135 | if (gettype($geometry) == 'object') { 136 | $simple_collections = array('MultiPoint','MultiLineString','MultiPolygon'); 137 | if (in_array(get_class($geometry),$passbacks)) { 138 | $components = $geometry->getComponents(); 139 | if (count($components) == 1) { 140 | return $components[0]; 141 | } 142 | else { 143 | return $geometry; 144 | } 145 | } 146 | } 147 | 148 | // So now we either have an array of geometries, a GeometryCollection, or an array of GeometryCollections 149 | if (!is_array($geometry)) { 150 | $geometry = array($geometry); 151 | } 152 | 153 | $geometries = array(); 154 | $geom_types = array(); 155 | 156 | $collections = array('MultiPoint','MultiLineString','MultiPolygon','GeometryCollection'); 157 | 158 | foreach ($geometry as $item) { 159 | if (in_array(get_class($item), $collections)) { 160 | foreach ($item->getComponents() as $component) { 161 | $geometries[] = $component; 162 | $geom_types[] = $component->geometryType(); 163 | } 164 | } 165 | else { 166 | $geometries[] = $item; 167 | $geom_types[] = $item->geometryType(); 168 | } 169 | } 170 | 171 | $geom_types = array_unique($geom_types); 172 | 173 | if (count($geom_types) == 1) { 174 | $class = 'Multi'.$geom_types[0]; 175 | return new $class($geometries); 176 | } 177 | else { 178 | return new GeometryCollection($geometries); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /geophp/lib/adapters/GPX.class.php: -------------------------------------------------------------------------------- 1 | geomFromText($gpx); 25 | } 26 | 27 | /** 28 | * Serialize geometries into a KML string. 29 | * 30 | * @param Geometry $geometry 31 | * 32 | * @return string The KML string representation of the input geometries 33 | */ 34 | public function write(Geometry $geometry) { 35 | return ''.$this->geometryToGPX($geometry).''; 36 | } 37 | 38 | public function geomFromText($text) { 39 | // Change to lower-case and strip all CDATA 40 | $text = strtolower($text); 41 | $text = preg_replace('//s','',$text); 42 | 43 | // Load into DOMDOcument 44 | $xmlobj = new DOMDocument(); 45 | $xmlobj->loadXML($text); 46 | if ($xmlobj === false) { 47 | throw new Exception("Invalid GPX: ". $text); 48 | } 49 | 50 | $this->xmlobj = $xmlobj; 51 | try { 52 | $geom = $this->geomFromXML(); 53 | } catch(InvalidText $e) { 54 | throw new Exception("Cannot Read Geometry From GPX: ". $text); 55 | } catch(Exception $e) { 56 | throw $e; 57 | } 58 | 59 | return $geom; 60 | } 61 | 62 | protected function geomFromXML() { 63 | $geometries = array(); 64 | $geometries = array_merge($geometries, $this->parseWaypoints()); 65 | $geometries = array_merge($geometries, $this->parseTracks()); 66 | $geometries = array_merge($geometries, $this->parseRoutes()); 67 | 68 | if (empty($geometries)) { 69 | throw new Exception("Invalid / Empty GPX"); 70 | } 71 | 72 | return geoPHP::geometryReduce($geometries); 73 | } 74 | 75 | protected function childElements($xml, $nodename = '') { 76 | $children = array(); 77 | foreach ($xml->childNodes as $child) { 78 | if ($child->nodeName == $nodename) { 79 | $children[] = $child; 80 | } 81 | } 82 | return $children; 83 | } 84 | 85 | protected function parseWaypoints() { 86 | $points = array(); 87 | $wpt_elements = $this->xmlobj->getElementsByTagName('wpt'); 88 | foreach ($wpt_elements as $wpt) { 89 | $lat = $wpt->attributes->getNamedItem("lat")->nodeValue; 90 | $lon = $wpt->attributes->getNamedItem("lon")->nodeValue; 91 | $points[] = new Point($lon, $lat); 92 | } 93 | return $points; 94 | } 95 | 96 | protected function parseTracks() { 97 | $lines = array(); 98 | $trk_elements = $this->xmlobj->getElementsByTagName('trk'); 99 | foreach ($trk_elements as $trk) { 100 | $components = array(); 101 | foreach ($this->childElements($trk, 'trkseg') as $trkseg) { 102 | foreach ($this->childElements($trkseg, 'trkpt') as $trkpt) { 103 | $lat = $trkpt->attributes->getNamedItem("lat")->nodeValue; 104 | $lon = $trkpt->attributes->getNamedItem("lon")->nodeValue; 105 | $components[] = new Point($lon, $lat); 106 | } 107 | } 108 | if ($components) {$lines[] = new LineString($components);} 109 | } 110 | return $lines; 111 | } 112 | 113 | protected function parseRoutes() { 114 | $lines = array(); 115 | $rte_elements = $this->xmlobj->getElementsByTagName('rte'); 116 | foreach ($rte_elements as $rte) { 117 | $components = array(); 118 | foreach ($this->childElements($rte, 'rtept') as $rtept) { 119 | $lat = $rtept->attributes->getNamedItem("lat")->nodeValue; 120 | $lon = $rtept->attributes->getNamedItem("lon")->nodeValue; 121 | $components[] = new Point($lon, $lat); 122 | } 123 | $lines[] = new LineString($components); 124 | } 125 | return $lines; 126 | } 127 | 128 | protected function geometryToGPX($geom) { 129 | $type = strtolower($geom->getGeomType()); 130 | switch ($type) { 131 | case 'point': 132 | return $this->pointToGPX($geom); 133 | break; 134 | case 'linestring': 135 | return $this->linestringToGPX($geom); 136 | break; 137 | case 'polygon': 138 | case 'multipoint': 139 | case 'multilinestring': 140 | case 'multipolygon': 141 | case 'geometrycollection': 142 | return $this->collectionToGPX($geom); 143 | break; 144 | } 145 | } 146 | 147 | private function pointToGPX($geom) { 148 | return ''; 149 | } 150 | 151 | private function linestringToGPX($geom) { 152 | $gpx = ''; 153 | 154 | foreach ($geom->getComponents() as $comp) { 155 | $gpx .= ''; 156 | } 157 | 158 | $gpx .= ''; 159 | 160 | return $gpx; 161 | } 162 | 163 | public function collectionToGPX($geom) { 164 | $gpx = ''; 165 | $components = $geom->getComponents(); 166 | foreach ($geom->getComponents() as $comp) { 167 | $gpx .= $this->geometryToGPX($comp); 168 | } 169 | 170 | return $gpx; 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /geophp/lib/adapters/GeoAdapter.class.php: -------------------------------------------------------------------------------- 1 | 4 | * (c) Patrick Hayes 5 | * 6 | * This code is open-source and licenced under the Modified BSD License. 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | /** 12 | * GeoJSON class : a geojson reader/writer. 13 | * 14 | * Note that it will always return a GeoJSON geometry. This 15 | * means that if you pass it a feature, it will return the 16 | * geometry of that feature strip everything else. 17 | */ 18 | class GeoJSON extends GeoAdapter 19 | { 20 | /** 21 | * Deserializes a GeoJSON into an object 22 | * 23 | * @param mixed $input The GeoJSON string or object 24 | * 25 | * @return object Geometry 26 | */ 27 | public function read($input) { 28 | if (is_string($input)) { 29 | $input = json_decode($input); 30 | } 31 | if (!is_object($input)) { 32 | throw new Exception('Invalid JSON'); 33 | } 34 | return self::toInstance($input); 35 | } 36 | 37 | /** 38 | * Serializes an object into a geojson string 39 | * 40 | * 41 | * @param Geometry $obj The object to serialize 42 | * 43 | * @return string The GeoJSON string 44 | */ 45 | public function write(Geometry $geometry) { 46 | if (is_null($geometry)) { 47 | return null; 48 | } 49 | 50 | return json_encode($geometry->getGeoInterface()); 51 | } 52 | 53 | /** 54 | * Converts an stdClass object into a Geometry based on its 'type' property 55 | * Converts an stdClass object into a Geometry, based on its 'type' property 56 | * 57 | * @param stdClass $obj Object resulting from json decoding a GeoJSON string 58 | * 59 | * @return object Object from class eometry 60 | */ 61 | static private function toInstance($obj) { 62 | if (is_null($obj)) { 63 | return null; 64 | } 65 | if (!isset($obj->type)) { 66 | self::checkType($obj); 67 | } 68 | 69 | if ($obj->type == 'Feature') { 70 | $instance = self::toGeomInstance($obj->geometry); 71 | } 72 | else if ($obj->type == 'FeatureCollection') { 73 | $geometries = array(); 74 | foreach ($obj->features as $feature) { 75 | $geometries[] = self::toGeomInstance($feature->geometry); 76 | } 77 | // Get a geometryCollection or MultiGeometry out of the the provided geometries 78 | $instance = geoPHP::geometryReduce($geometries); 79 | } 80 | else { 81 | // It's a geometry 82 | $instance = self::toGeomInstance($obj); 83 | } 84 | 85 | return $instance; 86 | } 87 | 88 | /** 89 | * Converts an stdClass object into a Geometry based on its 'type' property 90 | * 91 | * @param stdClass $obj Object resulting from json decoding a GeoJSON string 92 | * @param boolean $allowGeometryCollection Do we allow $obj to be a GeometryCollection ? 93 | * 94 | * @return object Object from class Geometry 95 | */ 96 | static private function toGeomInstance($obj, $allowGeometryCollection = true) { 97 | if (is_null($obj)) { 98 | return null; 99 | } 100 | 101 | self::checkType($obj); 102 | 103 | switch ($obj->type) { 104 | case 'Point': 105 | case 'LineString': 106 | case 'Polygon': 107 | self::checkExists($obj, 'coordinates', false, 'array'); 108 | $instance = call_user_func(array('self', 'to'.$obj->type), $obj->coordinates); 109 | break; 110 | 111 | case 'MultiPoint': 112 | case 'MultiLineString': 113 | case 'MultiPolygon': 114 | self::checkExists($obj, 'coordinates', false, 'array'); 115 | $items = array(); 116 | foreach ($obj->coordinates as $item) { 117 | $items[] = call_user_func(array('self', 'to'.substr($obj->type, 5)), $item); 118 | } 119 | $instance = new $obj->type($items); 120 | break; 121 | 122 | case 'GeometryCollection': 123 | if ($allowGeometryCollection) { 124 | self::checkExists($obj, 'geometries', false, 'array'); 125 | $geometries = array(); 126 | foreach ($obj->geometries as $geometry) { 127 | $geometries[] = self::toGeomInstance($geometry, false); 128 | } 129 | $instance = new GeometryCollection($geometries); 130 | } 131 | else { 132 | throw new Exception("Bad geojson: a GeometryCollection should not contain another GeometryCollection"); 133 | } 134 | break; 135 | 136 | default: 137 | throw new Exception("Unsupported object type ".$obj->type); 138 | } 139 | return $instance; 140 | } 141 | 142 | /** 143 | * Checks an object for type 144 | * 145 | * @param object $obj A geometry object 146 | * @param string $typeValue Value expected for 'type' property 147 | */ 148 | static private function checkType($obj, $typeValue = null) { 149 | if (!is_object($obj) || get_class($obj) != 'stdClass') { 150 | throw new Exception("Bad geojson"); 151 | } 152 | 153 | if (!isset($obj->type)) { 154 | throw new Exception("Bad geojson: Missing 'type' property"); 155 | } 156 | 157 | if (!is_null($typeValue) && $obj->type != $typeValue) { 158 | throw new Exception("Bad geojson: Unexpected 'type' value"); 159 | } 160 | } 161 | 162 | /** 163 | * Checks if a property exists inside an object 164 | * 165 | * @param object $obj An object 166 | * @param string $property The property to check 167 | * @param boolean $allowNull Whether to allow a null value or not (defaults to false) 168 | * @param string $type Check also $property type (object, array ...) 169 | */ 170 | static private function checkExists($obj, $property, $allowNull = false, $type = null) { 171 | if (!property_exists($obj, $property)) { 172 | throw new Exception("Bad geojson: Missing '$property' property"); 173 | } 174 | 175 | if (is_null($obj->$property)) { 176 | if (!$allowNull) { 177 | throw new Exception("Bad geojson: Null value for '$property' property"); 178 | } 179 | } 180 | else { 181 | switch ($type) { 182 | case null: 183 | break; 184 | case 'array': 185 | if (!is_array($obj->$property)) { 186 | throw new Exception("Bad geojson: Unexpected type for '$property' property"); 187 | } 188 | break; 189 | case 'object': 190 | if (!is_object($obj->$property)) { 191 | throw new Exception("Bad geojson: Unexpected type for '$property' property"); 192 | } 193 | break; 194 | default: 195 | throw new Exception("Unexpected error"); 196 | } 197 | } 198 | } 199 | 200 | /** 201 | * Converts an array of coordinates into a Point Geomtery 202 | * 203 | * @param array $coordinates The X/Y coordinates 204 | * 205 | * @return Point A Point object 206 | */ 207 | static private function toPoint(array $coordinates) { 208 | if (count($coordinates) == 2 && isset($coordinates[0]) && isset($coordinates[1])) { 209 | return new Point($coordinates[0], $coordinates[1]); 210 | } 211 | throw new Exception("Bad geojson: wrong point coordinates array"); 212 | } 213 | 214 | /** 215 | * Converts an array of coordinate arrays into a LineString Geometry 216 | * 217 | * @param array $coordinates The array of coordinates arrays (aka positions) 218 | * @return LineString A LineString object 219 | */ 220 | static private function toLineString(array $coordinates) { 221 | $positions = array(); 222 | foreach ($coordinates as $position) { 223 | $positions[] = self::toPoint($position); 224 | } 225 | return new LineString($positions); 226 | } 227 | 228 | /** 229 | * Converts an array of linestring coordinates into a Polygon Geometry 230 | * 231 | * @param array $coordinates The linestring coordinates 232 | * @return Polygon A Polygon object 233 | */ 234 | static private function toPolygon(array $coordinates) { 235 | $linestrings = array(); 236 | foreach ($coordinates as $linestring) { 237 | $linestrings[] = self::toLineString($linestring); 238 | } 239 | return new Polygon($linestrings); 240 | } 241 | 242 | } 243 | 244 | 245 | -------------------------------------------------------------------------------- /geophp/lib/adapters/GeoRSS.class.php: -------------------------------------------------------------------------------- 1 | geomFromText($gpx); 28 | } 29 | 30 | /** 31 | * Serialize geometries into a GeoRSS string. 32 | * 33 | * @param Geometry $geometry 34 | * 35 | * @return string The georss string representation of the input geometries 36 | */ 37 | public function write(Geometry $geometry, $namespace = FALSE) { 38 | if ($namespace) { 39 | $this->namespace = $namespace; 40 | $this->nss = $namespace.':'; 41 | } 42 | return $this->geometryToGeoRSS($geometry); 43 | } 44 | 45 | public function geomFromText($text) { 46 | // Change to lower-case, strip all CDATA, and de-namespace 47 | $text = strtolower($text); 48 | $text = preg_replace('//s','',$text); 49 | $text = str_replace('georss:','',$text); 50 | 51 | // Load into DOMDOcument 52 | $xmlobj = new DOMDocument(); 53 | $xmlobj->loadXML($text); 54 | if ($xmlobj === false) { 55 | throw new Exception("Invalid GeoRSS: ". $text); 56 | } 57 | 58 | $this->xmlobj = $xmlobj; 59 | try { 60 | $geom = $this->geomFromXML(); 61 | } catch(InvalidText $e) { 62 | throw new Exception("Cannot Read Geometry From GeoRSS: ". $text); 63 | } catch(Exception $e) { 64 | throw $e; 65 | } 66 | 67 | return $geom; 68 | } 69 | 70 | protected function geomFromXML() { 71 | $geometries = array(); 72 | $geometries = array_merge($geometries, $this->parsePoints()); 73 | $geometries = array_merge($geometries, $this->parseLines()); 74 | $geometries = array_merge($geometries, $this->parsePolygons()); 75 | $geometries = array_merge($geometries, $this->parseBoxes()); 76 | $geometries = array_merge($geometries, $this->parseCircles()); 77 | 78 | if (empty($geometries)) { 79 | throw new Exception("Invalid / Empty GeoRSS"); 80 | } 81 | 82 | return geoPHP::geometryReduce($geometries); 83 | } 84 | 85 | protected function getPointsFromCoords($string) { 86 | $coords = array(); 87 | $latlon = explode(' ',$string); 88 | foreach ($latlon as $key => $item) { 89 | if (!($key % 2)) { 90 | // It's a latitude 91 | $lat = $item; 92 | } 93 | else { 94 | // It's a longitude 95 | $lon = $item; 96 | $coords[] = new Point($lon, $lat); 97 | } 98 | } 99 | return $coords; 100 | } 101 | 102 | protected function parsePoints() { 103 | $points = array(); 104 | $pt_elements = $this->xmlobj->getElementsByTagName('point'); 105 | foreach ($pt_elements as $pt) { 106 | $point_array = $this->getPointsFromCoords(trim($pt->firstChild->nodeValue)); 107 | $points[] = $point_array[0]; 108 | } 109 | return $points; 110 | } 111 | 112 | protected function parseLines() { 113 | $lines = array(); 114 | $line_elements = $this->xmlobj->getElementsByTagName('line'); 115 | foreach ($line_elements as $line) { 116 | $components = $this->getPointsFromCoords(trim($line->firstChild->nodeValue)); 117 | $lines[] = new LineString($components); 118 | } 119 | return $lines; 120 | } 121 | 122 | protected function parsePolygons() { 123 | $polygons = array(); 124 | $poly_elements = $this->xmlobj->getElementsByTagName('polygon'); 125 | foreach ($poly_elements as $poly) { 126 | $points = $this->getPointsFromCoords(trim($poly->firstChild->nodeValue)); 127 | $exterior_ring = new LineString($points); 128 | $polygons[] = new Polygon(array($exterior_ring)); 129 | } 130 | return $polygons; 131 | } 132 | 133 | // Boxes are rendered into polygons 134 | protected function parseBoxes() { 135 | $polygons = array(); 136 | $box_elements = $this->xmlobj->getElementsByTagName('box'); 137 | foreach ($box_elements as $box) { 138 | $parts = explode(' ',trim($box->firstChild->nodeValue)); 139 | $components = array( 140 | new Point($parts[3], $parts[2]), 141 | new Point($parts[3], $parts[0]), 142 | new Point($parts[1], $parts[0]), 143 | new Point($parts[1], $parts[2]), 144 | new Point($parts[3], $parts[2]), 145 | ); 146 | $exterior_ring = new LineString($components); 147 | $polygons[] = new Polygon(array($exterior_ring)); 148 | } 149 | return $polygons; 150 | } 151 | 152 | // Circles are rendered into points 153 | // @@TODO: Add good support once we have circular-string geometry support 154 | protected function parseCircles() { 155 | $points = array(); 156 | $circle_elements = $this->xmlobj->getElementsByTagName('circle'); 157 | foreach ($circle_elements as $circle) { 158 | $parts = explode(' ',trim($circle->firstChild->nodeValue)); 159 | $points[] = new Point($parts[1], $parts[0]); 160 | } 161 | return $points; 162 | } 163 | 164 | protected function geometryToGeoRSS($geom) { 165 | $type = strtolower($geom->getGeomType()); 166 | switch ($type) { 167 | case 'point': 168 | return $this->pointToGeoRSS($geom); 169 | break; 170 | case 'linestring': 171 | return $this->linestringToGeoRSS($geom); 172 | break; 173 | case 'polygon': 174 | return $this->PolygonToGeoRSS($geom); 175 | break; 176 | case 'multipoint': 177 | case 'multilinestring': 178 | case 'multipolygon': 179 | case 'geometrycollection': 180 | return $this->collectionToGeoRSS($geom); 181 | break; 182 | } 183 | return $output; 184 | } 185 | 186 | private function pointToGeoRSS($geom) { 187 | return '<'.$this->nss.'point>'.$geom->getY().' '.$geom->getX().'nss.'point>'; 188 | } 189 | 190 | 191 | private function linestringToGeoRSS($geom) { 192 | $output = '<'.$this->nss.'line>'; 193 | foreach ($geom->getComponents() as $k => $point) { 194 | $output .= $point->getY().' '.$point->getX(); 195 | if ($k < ($geom->numGeometries() -1)) $output .= ' '; 196 | } 197 | $output .= 'nss.'line>'; 198 | return $output; 199 | } 200 | 201 | private function polygonToGeoRSS($geom) { 202 | $output = '<'.$this->nss.'polygon>'; 203 | $exterior_ring = $geom->exteriorRing(); 204 | foreach ($exterior_ring->getComponents() as $k => $point) { 205 | $output .= $point->getY().' '.$point->getX(); 206 | if ($k < ($exterior_ring->numGeometries() -1)) $output .= ' '; 207 | } 208 | $output .= 'nss.'polygon>'; 209 | return $output; 210 | } 211 | 212 | public function collectionToGeoRSS($geom) { 213 | $georss = '<'.$this->nss.'where>'; 214 | $components = $geom->getComponents(); 215 | foreach ($geom->getComponents() as $comp) { 216 | $georss .= $this->geometryToGeoRSS($comp); 217 | } 218 | 219 | $georss .= 'nss.'where>'; 220 | 221 | return $georss; 222 | } 223 | 224 | } 225 | -------------------------------------------------------------------------------- /geophp/lib/adapters/GoogleGeocode.class.php: -------------------------------------------------------------------------------- 1 | 4 | * (c) Patrick Hayes 5 | * 6 | * This code is open-source and licenced under the Modified BSD License. 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | /** 12 | * PHP Google Geocoder Adapter 13 | * 14 | * 15 | * @package geoPHP 16 | * @author Patrick Hayes 17 | */ 18 | class GoogleGeocode extends GeoAdapter 19 | { 20 | 21 | /** 22 | * Read an address string or array geometry objects 23 | * 24 | * @param string - Address to geocode 25 | * @param string - Type of Geometry to return. Can either be 'points' or 'bounds' (polygon) 26 | * @param Geometry|bounds-array - Limit the search area to within this region. For example 27 | * by default geocoding "Cairo" will return the location of Cairo Egypt. 28 | * If you pass a polygon of illinois, it will return Cairo IL. 29 | * @param return_multiple - Return all results in a multipoint or multipolygon 30 | * @return Geometry|GeometryCollection 31 | */ 32 | public function read($address, $return_type = 'point', $bounds = FALSE, $return_multiple = FALSE) { 33 | if (is_array($address)) $address = join(',', $address); 34 | 35 | if (gettype($bounds) == 'object') { 36 | $bounds = $bounds->getBBox(); 37 | } 38 | if (gettype($bounds) == 'array') { 39 | $bounds_string = '&bounds='.$bounds['miny'].','.$bounds['minx'].'|'.$bounds['maxy'].','.$bounds['maxx']; 40 | } 41 | else { 42 | $bounds_string = ''; 43 | } 44 | 45 | $url = "http://maps.googleapis.com/maps/api/geocode/json"; 46 | $url .= '?address='. urlencode($address); 47 | $url .= $bounds_string; 48 | $url .= '&sensor=false'; 49 | $this->result = json_decode(@file_get_contents($url)); 50 | 51 | if ($this->result->status == 'OK') { 52 | if ($return_multiple == FALSE) { 53 | if ($return_type == 'point') { 54 | return $this->getPoint(); 55 | } 56 | if ($return_type == 'bounds' || $return_type == 'polygon') { 57 | return $this->getPolygon(); 58 | } 59 | } 60 | if ($return_multiple == TRUE) { 61 | if ($return_type == 'point') { 62 | $points = array(); 63 | foreach ($this->result->results as $delta => $item) { 64 | $points[] = $this->getPoint($delta); 65 | } 66 | return new MultiPoint($points); 67 | } 68 | if ($return_type == 'bounds' || $return_type == 'polygon') { 69 | $polygons = array(); 70 | foreach ($this->result->results as $delta => $item) { 71 | $polygons[] = $this->getPolygon($delta); 72 | } 73 | return new MultiPolygon($polygons); 74 | } 75 | } 76 | } 77 | } 78 | 79 | /** 80 | * Serialize geometries into a WKT string. 81 | * 82 | * @param Geometry $geometry 83 | * @param string $return_type Should be either 'string' or 'array' 84 | * 85 | * @return string Does a reverse geocode of the geometry 86 | */ 87 | public function write(Geometry $geometry, $return_type = 'string') { 88 | $centroid = $geometry->getCentroid(); 89 | $lat = $centroid->getY(); 90 | $lon = $centroid->getX(); 91 | 92 | $url = "http://maps.googleapis.com/maps/api/geocode/json"; 93 | $url .= '?latlng='.$lat.','.$lon; 94 | $url .= '&sensor=false'; 95 | $this->result = json_decode(@file_get_contents($url)); 96 | 97 | if ($this->result->status == 'OK') { 98 | if ($return_type == 'string') { 99 | return $this->result->results[0]->formatted_address; 100 | } 101 | if ($return_type == 'array') { 102 | return $this->result->results[0]->address_components; 103 | } 104 | } 105 | } 106 | 107 | private function getPoint($delta = 0) { 108 | $lat = $this->result->results[$delta]->geometry->location->lat; 109 | $lon = $this->result->results[$delta]->geometry->location->lng; 110 | return new Point($lon, $lat); 111 | } 112 | 113 | private function getPolygon($delta = 0) { 114 | $points = array ( 115 | $this->getTopLeft($delta), 116 | $this->getTopRight($delta), 117 | $this->getBottomRight($delta), 118 | $this->getBottomLeft($delta), 119 | $this->getTopLeft($delta), 120 | ); 121 | $outer_ring = new LineString($points); 122 | return new Polygon(array($outer_ring)); 123 | } 124 | 125 | private function getTopLeft($delta = 0) { 126 | $lat = $this->result->results[$delta]->geometry->bounds->northeast->lat; 127 | $lon = $this->result->results[$delta]->geometry->bounds->southwest->lng; 128 | return new Point($lon, $lat); 129 | } 130 | 131 | private function getTopRight($delta = 0) { 132 | $lat = $this->result->results[$delta]->geometry->bounds->northeast->lat; 133 | $lon = $this->result->results[$delta]->geometry->bounds->northeast->lng; 134 | return new Point($lon, $lat); 135 | } 136 | 137 | private function getBottomLeft($delta = 0) { 138 | $lat = $this->result->results[$delta]->geometry->bounds->southwest->lat; 139 | $lon = $this->result->results[$delta]->geometry->bounds->southwest->lng; 140 | return new Point($lon, $lat); 141 | } 142 | 143 | private function getBottomRight($delta = 0) { 144 | $lat = $this->result->results[$delta]->geometry->bounds->southwest->lat; 145 | $lon = $this->result->results[$delta]->geometry->bounds->northeast->lng; 146 | return new Point($lon, $lat); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /geophp/lib/adapters/KML.class.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | class KML extends GeoAdapter 22 | { 23 | 24 | /** 25 | * Read KML string into geometry objects 26 | * 27 | * @param string $kml A KML string 28 | * 29 | * @return Geometry|GeometryCollection 30 | */ 31 | public function read($kml) { 32 | return $this->geomFromText($kml); 33 | } 34 | 35 | /** 36 | * Serialize geometries into a KML string. 37 | * 38 | * @param Geometry $geometry 39 | * 40 | * @return string The KML string representation of the input geometries 41 | */ 42 | public function write(Geometry $geometry) { 43 | return $this->geometryToKML($geometry); 44 | } 45 | 46 | public function geomFromText($text) { 47 | 48 | // Change to lower-case and strip all CDATA 49 | $text = mb_strtolower($text, mb_detect_encoding($text)); 50 | $text = preg_replace('//s','',$text); 51 | 52 | // Load into DOMDOcument 53 | $xmlobj = new DOMDocument(); 54 | @$xmlobj->loadXML($text); 55 | if ($xmlobj === false) { 56 | throw new Exception("Invalid KML: ". $text); 57 | } 58 | 59 | $this->xmlobj = $xmlobj; 60 | try { 61 | $geom = $this->geomFromXML(); 62 | } catch(InvalidText $e) { 63 | throw new Exception("Cannot Read Geometry From KML: ". $text); 64 | } catch(Exception $e) { 65 | throw $e; 66 | } 67 | 68 | return $geom; 69 | } 70 | 71 | protected function geomFromXML() { 72 | $geometries = array(); 73 | $geom_types = geoPHP::geometryList(); 74 | $placemark_elements = $this->xmlobj->getElementsByTagName('placemark'); 75 | if ($placemark_elements->length) { 76 | foreach ($placemark_elements as $placemark) { 77 | foreach ($placemark->childNodes as $child) { 78 | // Node names are all the same, except for MultiGeometry, which maps to GeometryCollection 79 | $node_name = $child->nodeName == 'multigeometry' ? 'geometrycollection' : $child->nodeName; 80 | if (array_key_exists($node_name, $geom_types)) { 81 | $function = 'parse'.$geom_types[$node_name]; 82 | $geometries[] = $this->$function($child); 83 | } 84 | } 85 | } 86 | } 87 | else { 88 | // The document does not have a placemark, try to create a valid geometry from the root element 89 | $node_name = $this->xmlobj->documentElement->nodeName == 'multigeometry' ? 'geometrycollection' : $this->xmlobj->documentElement->nodeName; 90 | if (array_key_exists($node_name, $geom_types)) { 91 | $function = 'parse'.$geom_types[$node_name]; 92 | $geometries[] = $this->$function($this->xmlobj->documentElement); 93 | } 94 | } 95 | return geoPHP::geometryReduce($geometries); 96 | } 97 | 98 | protected function childElements($xml, $nodename = '') { 99 | $children = array(); 100 | foreach ($xml->childNodes as $child) { 101 | if ($child->nodeName == $nodename) { 102 | $children[] = $child; 103 | } 104 | } 105 | return $children; 106 | } 107 | 108 | protected function parsePoint($xml) { 109 | $coordinates = $this->_extractCoordinates($xml); 110 | return new Point($coordinates[0][0],$coordinates[0][1]); 111 | } 112 | 113 | protected function parseLineString($xml) { 114 | $coordinates = $this->_extractCoordinates($xml); 115 | $point_array = array(); 116 | foreach ($coordinates as $set) { 117 | $point_array[] = new Point($set[0],$set[1]); 118 | } 119 | return new LineString($point_array); 120 | } 121 | 122 | protected function parsePolygon($xml) { 123 | $components = array(); 124 | 125 | $outer_boundary_element_a = $this->childElements($xml, 'outerboundaryis'); 126 | $outer_boundary_element = $outer_boundary_element_a[0]; 127 | $outer_ring_element_a = $this->childElements($outer_boundary_element, 'linearring'); 128 | $outer_ring_element = $outer_ring_element_a[0]; 129 | $components[] = $this->parseLineString($outer_ring_element); 130 | 131 | if (count($components) != 1) { 132 | throw new Exception("Invalid KML"); 133 | } 134 | 135 | $inner_boundary_element_a = $this->childElements($xml, 'innerboundaryis'); 136 | if (count($inner_boundary_element_a)) { 137 | $inner_boundary_element = $inner_boundary_element_a[0]; 138 | foreach ($this->childElements($inner_boundary_element, 'linearring') as $inner_ring_element) { 139 | $components[] = $this->parseLineString($inner_ring_element); 140 | } 141 | } 142 | 143 | return new Polygon($components); 144 | } 145 | 146 | protected function parseGeometryCollection($xml) { 147 | $components = array(); 148 | $geom_types = geoPHP::geometryList(); 149 | foreach ($xml->childNodes as $child) { 150 | $nodeName = ($child->nodeName == 'linearring') ? 'linestring' : $child->nodeName; 151 | $function = 'parse'.$geom_types[$nodeName]; 152 | $components[] = $this->$function($child); 153 | } 154 | return new GeometryCollection($components); 155 | } 156 | 157 | protected function _extractCoordinates($xml) { 158 | $coord_elements = $this->childElements($xml, 'coordinates'); 159 | if (!count($coord_elements)) { 160 | throw new Exception('Bad KML: Missing coordinate element'); 161 | } 162 | $coordinates = array(); 163 | $coord_sets = explode(' ',$coord_elements[0]->nodeValue); 164 | foreach ($coord_sets as $set_string) { 165 | $set_string = trim($set_string); 166 | if ($set_string) { 167 | $set_array = explode(',',$set_string); 168 | if (count($set_array) >= 2) { 169 | $coordinates[] = $set_array; 170 | } 171 | } 172 | } 173 | 174 | return $coordinates; 175 | } 176 | 177 | private function geometryToKML($geom) { 178 | $type = strtolower($geom->getGeomType()); 179 | switch ($type) { 180 | case 'point': 181 | return $this->pointToKML($geom); 182 | break; 183 | case 'linestring': 184 | return $this->linestringToKML($geom); 185 | break; 186 | case 'polygon': 187 | return $this->polygonToKML($geom); 188 | break; 189 | case 'multipoint': 190 | case 'multilinestring': 191 | case 'multipolygon': 192 | case 'geometrycollection': 193 | return $this->collectionToKML($geom); 194 | break; 195 | } 196 | } 197 | 198 | private function pointToKML($geom) { 199 | return "".$geom->getX().",".$geom->getY().""; 200 | } 201 | 202 | private function linestringToKML($geom, $type = FALSE) { 203 | if (!$type) { 204 | $type = $geom->getGeomType(); 205 | } 206 | 207 | $str = '<'. $type .'>'; 208 | $i=0; 209 | foreach ($geom->getComponents() as $comp) { 210 | if ($i != 0) $str .= ' '; 211 | $str .= $comp->getX() .','. $comp->getY(); 212 | $i++; 213 | } 214 | 215 | return $str .''; 216 | } 217 | 218 | public function polygonToKML($geom) { 219 | $components = $geom->getComponents(); 220 | $str = '' . $this->linestringToKML($components[0], 'LinearRing') . ''; 221 | foreach (array_slice($components, 1) as $comp) { 222 | $str .= '' . $this->linestringToKML($comp) . ''; 223 | } 224 | 225 | return ''. $str .''; 226 | } 227 | 228 | public function collectionToKML($geom) { 229 | $components = $geom->getComponents(); 230 | $str = ''; 231 | foreach ($geom->getComponents() as $comp) { 232 | $sub_adapter = new KML(); 233 | $str .= $sub_adapter->write($comp); 234 | } 235 | 236 | return $str .''; 237 | } 238 | 239 | } 240 | -------------------------------------------------------------------------------- /geophp/lib/adapters/WKB.class.php: -------------------------------------------------------------------------------- 1 | getGeometry($mem); 34 | fclose($mem); 35 | return $geometry; 36 | } 37 | 38 | function getGeometry(&$mem) { 39 | $base_info = unpack("corder/Ltype", fread($mem, 5)); 40 | if ($base_info['order'] !== 1) { 41 | throw new Exception('Only NDR (little endian) SKB format is supported at the moment'); 42 | } 43 | switch ($base_info['type']) { 44 | case 1: 45 | return $this->getPoint($mem); 46 | case 2: 47 | return $this->getLinstring($mem); 48 | case 3: 49 | return $this->getPolygon($mem); 50 | case 4: 51 | return $this->getMulti($mem,'point'); 52 | case 5: 53 | return $this->getMulti($mem,'line'); 54 | case 6: 55 | return $this->getMulti($mem,'polygon'); 56 | case 7: 57 | return $this->getMulti($mem,'geometry'); 58 | } 59 | } 60 | 61 | function getPoint(&$mem) { 62 | $point_coords = unpack("d*", fread($mem,16)); 63 | return new Point($point_coords[1],$point_coords[2]); 64 | } 65 | 66 | function getLinstring(&$mem) { 67 | // Get the number of points expected in this string out of the first 4 bytes 68 | $line_length = unpack('L',fread($mem,4)); 69 | 70 | // Read the nubmer of points x2 (each point is two coords) into decimal-floats 71 | $line_coords = unpack('d'.$line_length[1]*2, fread($mem,$line_length[1]*16)); 72 | 73 | // We have our coords, build up the linestring 74 | $components = array(); 75 | $i = 1; 76 | $num_coords = count($line_coords); 77 | while ($i <= $num_coords) { 78 | $components[] = new Point($line_coords[$i],$line_coords[$i+1]); 79 | $i += 2; 80 | } 81 | return new LineString($components); 82 | } 83 | 84 | function getPolygon(&$mem) { 85 | // Get the number of linestring expected in this poly out of the first 4 bytes 86 | $poly_length = unpack('L',fread($mem,4)); 87 | 88 | $components = array(); 89 | $i = 1; 90 | while ($i <= $poly_length[1]) { 91 | $components[] = $this->getLinstring($mem); 92 | $i++; 93 | } 94 | return new Polygon($components); 95 | } 96 | 97 | function getMulti(&$mem, $type) { 98 | // Get the number of items expected in this multi out of the first 4 bytes 99 | $multi_length = unpack('L',fread($mem,4)); 100 | 101 | //@@TODO: create an EMPTY geometry instead of returning null 102 | if (!$multi_length[1]) return NULL; 103 | 104 | $components = array(); 105 | $i = 1; 106 | while ($i <= $multi_length[1]) { 107 | $components[] = $this->getGeometry($mem); 108 | $i++; 109 | } 110 | switch ($type) { 111 | case 'point': 112 | return new MultiPoint($components); 113 | case 'line': 114 | return new MultiLineString($components); 115 | case 'polygon': 116 | return new MultiPolygon($components); 117 | case 'geometry': 118 | return new GeometryCollection($components); 119 | } 120 | } 121 | 122 | /** 123 | * Serialize geometries into WKB string. 124 | * 125 | * @param Geometry $geometry 126 | * 127 | * @return string The WKB string representation of the input geometries 128 | */ 129 | public function write(Geometry $geometry, $write_as_hex = FALSE) { 130 | // We always write into NDR (little endian) 131 | $wkb = pack('c',1); 132 | 133 | switch ($geometry->getGeomType()) { 134 | case 'Point'; 135 | $wkb .= pack('L',1); 136 | $wkb .= $this->writePoint($geometry); 137 | break; 138 | case 'LineString'; 139 | $wkb .= pack('L',2); 140 | $wkb .= $this->writeLineString($geometry); 141 | break; 142 | case 'Polygon'; 143 | $wkb .= pack('L',3); 144 | $wkb .= $this->writePolygon($geometry); 145 | break; 146 | case 'MultiPoint'; 147 | $wkb .= pack('L',4); 148 | $wkb .= $this->writeMulti($geometry); 149 | break; 150 | case 'MultiLineString'; 151 | $wkb .= pack('L',5); 152 | $wkb .= $this->writeMulti($geometry); 153 | break; 154 | case 'MultiPolygon'; 155 | $wkb .= pack('L',6); 156 | $wkb .= $this->writeMulti($geometry); 157 | break; 158 | case 'GeometryCollection'; 159 | $wkb .= pack('L',7); 160 | $wkb .= $this->writeMulti($geometry); 161 | break; 162 | } 163 | 164 | if ($write_as_hex) { 165 | $unpacked = unpack('H*',$wkb); 166 | return $unpacked[1]; 167 | } 168 | else { 169 | return $wkb; 170 | } 171 | } 172 | 173 | function writePoint($point) { 174 | // Set the coords 175 | $wkb = pack('dd',$point->x(), $point->y()); 176 | 177 | return $wkb; 178 | } 179 | 180 | function writeLineString($line) { 181 | // Set the number of points in this line 182 | $wkb = pack('L',$line->numPoints()); 183 | 184 | // Set the coords 185 | foreach ($line->getComponents() as $point) { 186 | $wkb .= pack('dd',$point->x(), $point->y()); 187 | } 188 | 189 | return $wkb; 190 | } 191 | 192 | function writePolygon($poly) { 193 | // Set the number of lines in this poly 194 | $wkb = pack('L',$poly->numGeometries()); 195 | 196 | // Write the lines 197 | foreach ($poly->getComponents() as $line) { 198 | $wkb .= $this->writeLineString($line); 199 | } 200 | 201 | return $wkb; 202 | } 203 | 204 | function writeMulti($geometry) { 205 | // Set the number of components 206 | $wkb = pack('L',$geometry->numGeometries()); 207 | 208 | // Write the components 209 | foreach ($geometry->getComponents() as $component) { 210 | $wkb .= $this->write($component); 211 | } 212 | 213 | return $wkb; 214 | } 215 | 216 | } 217 | -------------------------------------------------------------------------------- /geophp/lib/adapters/WKT.class.php: -------------------------------------------------------------------------------- 1 | 4 | * (c) Patrick Hayes 5 | * 6 | * This code is open-source and licenced under the Modified BSD License. 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | /** 12 | * PHP Geometry/WKT encoder/decoder 13 | * 14 | * Mainly inspired/adapted from OpenLayers( http://www.openlayers.org ) 15 | * Openlayers/format/WKT.js 16 | * 17 | * @package sfMapFishPlugin 18 | * @subpackage GeoJSON 19 | * @author Camptocamp 20 | */ 21 | class WKT extends GeoAdapter 22 | { 23 | 24 | private $regExes = array( 25 | 'typeStr' => '/^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/', 26 | 'spaces' => '/\s+/', 27 | 'parenComma' => '/\)\s*,\s*\(/', 28 | 'doubleParenComma' => '/\)\s*\)\s*,\s*\(\s*\(/', 29 | 'trimParens' => '/^\s*\(?(.*?)\)?\s*$/' 30 | ); 31 | 32 | const POINT = 'point'; 33 | const MULTIPOINT = 'multipoint'; 34 | const LINESTRING = 'linestring'; 35 | const MULTILINESTRING = 'multilinestring'; 36 | const POLYGON = 'polygon'; 37 | const MULTIPOLYGON = 'multipolygon'; 38 | const GEOMETRYCOLLECTION = 'geometrycollection'; 39 | 40 | /** 41 | * Read WKT string into geometry objects 42 | * 43 | * @param string $WKT A WKT string 44 | * 45 | * @return Geometry|GeometryCollection 46 | */ 47 | public function read($wkt) { 48 | $wkt = strval($wkt); 49 | 50 | // If geos is installed, then we take a shortcut and let it parse the WKT 51 | if (geoPHP::geosInstalled()) { 52 | $reader = new GEOSWKTReader(); 53 | return geoPHP::geosToGeometry($reader->read($wkt)); 54 | } 55 | 56 | $matches = array(); 57 | if (!preg_match($this->regExes['typeStr'], $wkt, $matches)) { 58 | return null; 59 | } 60 | 61 | return $this->parse(strtolower($matches[1]), $matches[2]); 62 | } 63 | 64 | /** 65 | * Serialize geometries into a WKT string. 66 | * 67 | * @param Geometry $geometry 68 | * 69 | * @return string The WKT string representation of the input geometries 70 | */ 71 | public function write(Geometry $geometry) { 72 | // If geos is installed, then we take a shortcut and let it write the WKT 73 | if (geoPHP::geosInstalled()) { 74 | $writer = new GEOSWKTWriter(); 75 | return $writer->write($geometry->geos()); 76 | } 77 | 78 | $type = strtolower($geometry->geometryType()); 79 | 80 | if (is_null($data = $this->extract($geometry))) { 81 | return null; 82 | } 83 | 84 | return strtoupper($type).' ('.$data.')'; 85 | } 86 | 87 | /** 88 | * Parse WKT string into geometry objects 89 | * 90 | * @param string $WKT A WKT string 91 | * 92 | * @return Geometry|GeometryCollection 93 | */ 94 | public function parse($type, $str) { 95 | $matches = array(); 96 | $components = array(); 97 | 98 | switch ($type) { 99 | case self::POINT: 100 | $coords = $this->pregExplode('spaces', $str); 101 | return new Point(floatval($coords[0]), floatval($coords[1])); 102 | 103 | case self::MULTIPOINT: 104 | $points = $this->pregExplode('parenComma', $str); 105 | foreach ($points as $p) { 106 | $point = $this->trimParens( $p ); 107 | $components[] = $this->parse(self::POINT, $point); 108 | } 109 | return new MultiPoint($components); 110 | 111 | case self::LINESTRING: 112 | foreach (explode(',', trim($str)) as $point) { 113 | $components[] = $this->parse(self::POINT, $point); 114 | } 115 | return new LineString($components); 116 | 117 | case self::MULTILINESTRING: 118 | $lines = $this->pregExplode('parenComma', $str); 119 | foreach ($lines as $l) { 120 | $line = $this->trimParens( $l ); 121 | $components[] = $this->parse(self::LINESTRING, $line); 122 | } 123 | return new MultiLineString($components); 124 | 125 | case self::POLYGON: 126 | $rings= $this->pregExplode('parenComma', $str); 127 | foreach ($rings as $r) { 128 | $ring = $this->trimParens( $r ); 129 | $linestring = $this->parse(self::LINESTRING, $ring); 130 | $components[] = new LineString($linestring->getComponents()); 131 | } 132 | return new Polygon($components); 133 | 134 | case self::MULTIPOLYGON: 135 | $polygons = $this->pregExplode('doubleParenComma', $str); 136 | foreach ($polygons as $p) { 137 | $polygon = $this->trimParens( $p ); 138 | $components[] = $this->parse(self::POLYGON, $polygon); 139 | } 140 | return new MultiPolygon($components); 141 | 142 | case self::GEOMETRYCOLLECTION: 143 | $str = preg_replace('/,\s*([A-Za-z])/', '|$1', $str); 144 | $wktArray = explode('|', trim($str)); 145 | foreach ($wktArray as $wkt) { 146 | $components[] = $this->read($wkt); 147 | } 148 | return new GeometryCollection($components); 149 | 150 | default: 151 | return null; 152 | } 153 | } 154 | 155 | /** 156 | * Trim the parenthesis 157 | * 158 | */ 159 | protected function trimParens($str) { 160 | $open_parent = stripos( $str, '(' ); 161 | $open_parent = ($open_parent!==false)?$open_parent+1:0; 162 | $close_parent = strripos( $str, ')' ); 163 | $close_parent = ($close_parent!==false)?$close_parent:strlen($str); 164 | return substr( $str, $open_parent, $close_parent); 165 | } 166 | 167 | /** 168 | * Split string according to first match of passed regEx index of $regExes 169 | * 170 | */ 171 | protected function pregExplode($regEx, $str) { 172 | $matches = array(); 173 | preg_match($this->regExes[$regEx], $str, $matches); 174 | return empty($matches)?array(trim($str)):explode($matches[0], trim($str)); 175 | } 176 | 177 | /** 178 | * Extract geometry to a WKT string 179 | * 180 | * @param Geometry $geometry A Geometry object 181 | * 182 | * @return strin 183 | */ 184 | public function extract(Geometry $geometry) { 185 | $array = array(); 186 | switch (strtolower(get_class($geometry))) { 187 | case self::POINT: 188 | return $geometry->getX().' '.$geometry->getY(); 189 | case self::LINESTRING: 190 | foreach ($geometry as $geom) { 191 | $array[] = $this->extract($geom); 192 | } 193 | return implode(', ', $array); 194 | case self::MULTIPOINT: 195 | case self::MULTILINESTRING: 196 | case self::POLYGON: 197 | case self::MULTIPOLYGON: 198 | foreach ($geometry as $geom) { 199 | $array[] = '('.$this->extract($geom).')'; 200 | } 201 | return implode(', ', $array); 202 | case self::GEOMETRYCOLLECTION: 203 | foreach ($geometry as $geom) { 204 | $array[] = strtoupper(get_class($geom)).' ('.$this->extract($geom).')'; 205 | } 206 | return implode(', ', $array); 207 | default: 208 | return null; 209 | } 210 | } 211 | 212 | /** 213 | * Loads a WKT string into a Geometry Object 214 | * 215 | * @param string $WKT 216 | * 217 | * @return Geometry 218 | */ 219 | static public function load($WKT) { 220 | $instance = new self; 221 | return $instance->read($WKT); 222 | } 223 | 224 | } 225 | -------------------------------------------------------------------------------- /geophp/lib/geometry/Collection.class.php: -------------------------------------------------------------------------------- 1 | 4 | * (c) Patrick Hayes 5 | * 6 | * This code is open-source and licenced under the Modified BSD License. 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | /** 12 | * Collection : abstract class which represents a collection of components. 13 | * 14 | * @package sfMapFishPlugin 15 | * @subpackage GeoJSON 16 | * @author Camptocamp 17 | * @version 18 | */ 19 | abstract class Collection extends Geometry implements Iterator 20 | { 21 | protected $components = array(); 22 | 23 | /** 24 | * Constructor 25 | * 26 | * @param array $components The components array 27 | */ 28 | public function __construct(array $components) { 29 | if (empty($components)) { 30 | throw new Exception("Cannot create empty collection"); 31 | } 32 | 33 | foreach ($components as $component) 34 | { 35 | $this->add($component); 36 | } 37 | } 38 | 39 | // Iterator Interface functions 40 | // ---------------------------- 41 | public function rewind() { 42 | reset($this->components); 43 | } 44 | 45 | public function current() { 46 | return current($this->components); 47 | } 48 | 49 | public function key() { 50 | return key($this->components); 51 | } 52 | 53 | public function next() { 54 | return next($this->components); 55 | } 56 | 57 | private function add($component) { 58 | $this->components[] = $component; 59 | } 60 | 61 | public function valid() { 62 | return $this->current() !== false; 63 | } 64 | 65 | /** 66 | * An accessor method which recursively calls itself to build the coordinates array 67 | * 68 | * @return array The coordinates array 69 | */ 70 | public function getCoordinates() { 71 | $coordinates = array(); 72 | foreach ($this->components as $component) 73 | { 74 | $coordinates[] = $component->getCoordinates(); 75 | } 76 | return $coordinates; 77 | } 78 | 79 | /** 80 | * Returns Colection components 81 | * 82 | * @return array 83 | */ 84 | public function getComponents() { 85 | return $this->components; 86 | } 87 | 88 | public function centroid() { 89 | if ($this->geos()) { 90 | $geos_centroid = $this->geos()->centroid(); 91 | if ($geos_centroid->typeName() == 'Point') { 92 | return geoPHP::geosToGeometry($this->geos()->centroid()); 93 | } 94 | } 95 | 96 | // As a rough estimate, we say that the centroid of a colletion is the centroid of it's envelope 97 | // @@TODO: Make this the centroid of the convexHull 98 | // Note: Outside of polygons, geometryCollections and the trivial case of points, there is no standard on what a "centroid" is 99 | $centroid = $this->envelope()->centroid(); 100 | 101 | return $centroid; 102 | } 103 | 104 | public function getBBox() { 105 | if ($this->geos()) { 106 | $envelope = $this->geos()->envelope(); 107 | if ($envelope->typeName() == 'Point') { 108 | return geoPHP::geosToGeometry($envelope)->getBBOX(); 109 | } 110 | 111 | $geos_ring = $envelope->exteriorRing(); 112 | return array( 113 | 'maxy' => $geos_ring->pointN(3)->getY(), 114 | 'miny' => $geos_ring->pointN(1)->getY(), 115 | 'maxx' => $geos_ring->pointN(1)->getX(), 116 | 'minx' => $geos_ring->pointN(3)->getX(), 117 | ); 118 | } 119 | 120 | // Go through each component and get the max and min x and y 121 | $i = 0; 122 | foreach ($this->components as $component) { 123 | $component_bbox = $component->getBBox(); 124 | 125 | // On the first run through, set the bbox to the component bbox 126 | if ($i == 0) { 127 | $maxx = $component_bbox['maxx']; 128 | $maxy = $component_bbox['maxy']; 129 | $minx = $component_bbox['minx']; 130 | $miny = $component_bbox['miny']; 131 | } 132 | 133 | // Do a check and replace on each boundary, slowly growing the bbox 134 | $maxx = $component_bbox['maxx'] > $maxx ? $component_bbox['maxx'] : $maxx; 135 | $maxy = $component_bbox['maxy'] > $maxy ? $component_bbox['maxy'] : $maxy; 136 | $minx = $component_bbox['minx'] < $minx ? $component_bbox['minx'] : $minx; 137 | $miny = $component_bbox['miny'] < $miny ? $component_bbox['miny'] : $miny; 138 | $i++; 139 | } 140 | 141 | return array( 142 | 'maxy' => $maxy, 143 | 'miny' => $miny, 144 | 'maxx' => $maxx, 145 | 'minx' => $minx, 146 | ); 147 | } 148 | 149 | public function area() { 150 | if ($this->geos()) { 151 | return $this->geos()->area(); 152 | } 153 | 154 | $area = 0; 155 | foreach ($this->components as $component) { 156 | $area += $component->area(); 157 | } 158 | return $area; 159 | } 160 | 161 | // By default, the boundary of a collection is the boundary of it's components 162 | public function boundary() { 163 | if ($this->geos()) { 164 | return $this->geos()->boundary(); 165 | } 166 | 167 | $components_boundaries = array(); 168 | foreach ($this->components as $component) { 169 | $components_boundaries[] = $component->boundary(); 170 | } 171 | return geoPHP::geometryReduce($components_boundaries); 172 | } 173 | 174 | public function numGeometries() { 175 | return count($this->components); 176 | } 177 | 178 | // Note that the standard is 1 based indexing 179 | public function geometryN($n) { 180 | $n = intval($n); 181 | if (array_key_exists($n-1, $this->components)) { 182 | return $this->components[$n-1]; 183 | } 184 | else { 185 | return NULL; 186 | } 187 | } 188 | 189 | public function length() { 190 | if ($this->geos()) { 191 | return $this->geos()->length(); 192 | } 193 | 194 | $length = 0; 195 | foreach ($this->components as $delta => $point) { 196 | $next_point = $this->geometryN($delta); 197 | if ($next_point) { 198 | // Pythagorean Theorem 199 | $distance = sqrt(($next_point->getX() - $point->getX())^2+($next_point->getY()- $point->getY())^2); 200 | $length += $distance; 201 | } 202 | } 203 | return $length; 204 | } 205 | 206 | public function dimension() { 207 | $dimension = 0; 208 | foreach ($this->components as $component) { 209 | if ($component->dimension() > $dimension) { 210 | $dimension = $component->dimension(); 211 | } 212 | } 213 | return $dimension; 214 | } 215 | 216 | 217 | // Not valid for this geometry type 218 | // -------------------------------- 219 | public function x() { return NULL; } 220 | public function y() { return NULL; } 221 | public function startPoint() { return NULL; } 222 | public function endPoint() { return NULL; } 223 | public function isRing() { return NULL; } 224 | public function isClosed() { return NULL; } 225 | public function numPoints() { return NULL; } 226 | public function pointN($n) { return NULL; } 227 | public function exteriorRing() { return NULL; } 228 | public function numInteriorRings() { return NULL; } 229 | public function interiorRingN($n) { return NULL; } 230 | public function pointOnSurface() { return NULL; } 231 | 232 | } 233 | 234 | -------------------------------------------------------------------------------- /geophp/lib/geometry/Geometry.class.php: -------------------------------------------------------------------------------- 1 | 4 | * (c) Patrick Hayes 5 | * 6 | * This code is open-source and licenced under the Modified BSD License. 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | /** 12 | * Geometry : abstract class which represents a geometry. 13 | * 14 | * @package sfMapFishPlugin 15 | * @subpackage GeoJSON 16 | * @author Camptocamp 17 | * @version 18 | */ 19 | abstract class Geometry 20 | { 21 | private $geos = NULL; 22 | protected $geom_type; 23 | protected $srid; 24 | 25 | 26 | // Abtract: Standard 27 | // ----------------- 28 | abstract public function area(); 29 | abstract public function boundary(); 30 | abstract public function centroid(); 31 | abstract public function length(); 32 | abstract public function y(); 33 | abstract public function x(); 34 | abstract public function numGeometries(); 35 | abstract public function geometryN($n); 36 | abstract public function startPoint(); 37 | abstract public function endPoint(); 38 | abstract public function isRing(); // Mssing dependancy 39 | abstract public function isClosed(); // Missing dependancy 40 | abstract public function numPoints(); 41 | abstract public function pointN($n); 42 | abstract public function exteriorRing(); 43 | abstract public function numInteriorRings(); 44 | abstract public function interiorRingN($n); 45 | abstract public function dimension(); 46 | 47 | 48 | // Abtract: Non-Standard 49 | // --------------------- 50 | abstract public function getCoordinates(); 51 | abstract public function getBBox(); 52 | 53 | 54 | // Public: Standard -- Common to all geometries 55 | // -------------------------------------------- 56 | public function SRID() { 57 | return $this->srid; 58 | } 59 | 60 | public function setSRID($srid) { 61 | if ($this->geos()) { 62 | $this->geos()->setSRID($srid); 63 | } 64 | $this->srid = $srid; 65 | } 66 | 67 | public function envelope() { 68 | if ($this->geos()) { 69 | return geoPHP::geosToGeometry($this->geos()->envelope()); 70 | } 71 | 72 | $bbox = $this->getBBox(); 73 | $points = array ( 74 | new Point($bbox['maxx'],$bbox['miny']), 75 | new Point($bbox['maxx'],$bbox['maxy']), 76 | new Point($bbox['minx'],$bbox['maxy']), 77 | new Point($bbox['minx'],$bbox['miny']), 78 | new Point($bbox['maxx'],$bbox['miny']), 79 | ); 80 | 81 | $outer_boundary = new LineString($points); 82 | return new Polygon(array($outer_boundary)); 83 | } 84 | 85 | public function geometryType() { 86 | return $this->geom_type; 87 | } 88 | 89 | // Public: Non-Standard -- Common to all geometries 90 | // ------------------------------------------------ 91 | public function getGeoInterface() { 92 | return array( 93 | 'type'=> $this->getGeomType(), 94 | 'coordinates'=> $this->getCoordinates() 95 | ); 96 | } 97 | 98 | // $this->out($format, $other_args); 99 | public function out() { 100 | $args = func_get_args(); 101 | 102 | $format = array_shift($args); 103 | $type_map = geoPHP::getAdapterMap(); 104 | $processor_type = $type_map[$format]; 105 | $processor = new $processor_type(); 106 | 107 | array_unshift($args, $this); 108 | $result = call_user_func_array(array($processor, 'write'), $args); 109 | 110 | return $result; 111 | } 112 | 113 | 114 | // Public: Aliases 115 | // --------------- 116 | public function getCentroid() { 117 | return $this->centroid(); 118 | } 119 | 120 | public function getArea() { 121 | return $this->area(); 122 | } 123 | 124 | public function getX() { 125 | return $this->x(); 126 | } 127 | 128 | public function getY() { 129 | return $this->y(); 130 | } 131 | 132 | public function getGeos() { 133 | return $this->geos(); 134 | } 135 | 136 | public function getGeomType() { 137 | return $this->geometryType(); 138 | } 139 | 140 | public function getSRID() { 141 | return $this->SRID(); 142 | } 143 | 144 | public function asText() { 145 | return $this->out('wkt'); 146 | } 147 | 148 | public function asBinary() { 149 | return $this->out('wkb'); 150 | } 151 | 152 | // Public: GEOS Only Functions 153 | // --------------------------- 154 | public function geos() { 155 | // If it's already been set, just return it 156 | if ($this->geos && geoPHP::geosInstalled()) { 157 | return $this->geos; 158 | } 159 | // It hasn't been set yet, generate it 160 | if (geoPHP::geosInstalled()) { 161 | $reader = new GEOSWKBReader(); 162 | $this->geos = $reader->readHEX($this->out('wkb',TRUE)); 163 | } 164 | else { 165 | $this->geos = FALSE; 166 | } 167 | return $this->geos; 168 | } 169 | 170 | public function setGeos($geos) { 171 | $this->geos = $geos; 172 | } 173 | 174 | public function pointOnSurface() { 175 | if ($this->geos()) { 176 | return geoPHP::geosToGeometry($this->geos()->pointOnSurface()); 177 | } 178 | } 179 | 180 | public function equals($geometry) { 181 | if ($this->geos()) { 182 | return $this->geos()->equals($geometry->geos()); 183 | } 184 | } 185 | 186 | public function equalsExact($geometry) { 187 | if ($this->geos()) { 188 | return $this->geos()->equalsExact($geometry->geos()); 189 | } 190 | } 191 | 192 | public function relate($geometry) { 193 | //@@TODO: Deal with second "pattern" parameter 194 | if ($this->geos()) { 195 | return $this->geos()->relate($geometry->geos()); 196 | } 197 | } 198 | 199 | public function checkValidity() { 200 | if ($this->geos()) { 201 | return $this->geos()->checkValidity(); 202 | } 203 | } 204 | 205 | public function isSimple() { 206 | if ($this->geos()) { 207 | return $this->geos()->isSimple(); 208 | } 209 | } 210 | 211 | public function buffer($distance) { 212 | if ($this->geos()) { 213 | return geoPHP::geosToGeometry($this->geos()->buffer($distance)); 214 | } 215 | } 216 | 217 | public function intersection($geometry) { 218 | if ($this->geos()) { 219 | return geoPHP::geosToGeometry($this->geos()->intersection($geometry->geos())); 220 | } 221 | } 222 | 223 | public function convexHull() { 224 | if ($this->geos()) { 225 | return geoPHP::geosToGeometry($this->geos()->convexHull()); 226 | } 227 | } 228 | 229 | public function difference($geometry) { 230 | if ($this->geos()) { 231 | return geoPHP::geosToGeometry($this->geos()->difference($geometry->geos())); 232 | } 233 | } 234 | 235 | public function symDifference($geometry) { 236 | if ($this->geos()) { 237 | return geoPHP::geosToGeometry($this->geos()->symDifference($geometry->geos())); 238 | } 239 | } 240 | 241 | public function union($geometry) { 242 | //@@TODO: also does union cascade 243 | if ($this->geos()) { 244 | return geoPHP::geosToGeometry($this->geos()->union($geometry->geos())); 245 | } 246 | } 247 | 248 | public function simplify($tolerance, $preserveTopology = FALSE) { 249 | if ($this->geos()) { 250 | return geoPHP::geosToGeometry($this->geos()->simplify($tolerance, $preserveTopology)); 251 | } 252 | } 253 | 254 | public function disjoint($geometry) { 255 | if ($this->geos()) { 256 | return $this->geos()->disjoint($geometry->geos()); 257 | } 258 | } 259 | 260 | public function touches($geometry) { 261 | if ($this->geos()) { 262 | return $this->geos()->touches($geometry->geos()); 263 | } 264 | } 265 | 266 | public function intersects($geometry) { 267 | if ($this->geos()) { 268 | return $this->geos()->intersects($geometry->geos()); 269 | } 270 | } 271 | 272 | public function crosses($geometry) { 273 | if ($this->geos()) { 274 | return $this->geos()->crosses($geometry->geos()); 275 | } 276 | } 277 | 278 | public function within($geometry) { 279 | if ($this->geos()) { 280 | return $this->geos()->within($geometry->geos()); 281 | } 282 | } 283 | 284 | public function contains($geometry) { 285 | if ($this->geos()) { 286 | return $this->geos()->contains($geometry->geos()); 287 | } 288 | } 289 | 290 | public function overlaps($geometry) { 291 | if ($this->geos()) { 292 | return $this->geos()->overlaps($geometry->geos()); 293 | } 294 | } 295 | 296 | public function covers($geometry) { 297 | if ($this->geos()) { 298 | return $this->geos()->covers($geometry->geos()); 299 | } 300 | } 301 | 302 | public function coveredBy($geometry) { 303 | if ($this->geos()) { 304 | return $this->geos()->coveredBy($geometry->geos()); 305 | } 306 | } 307 | 308 | public function distance($geometry) { 309 | if ($this->geos()) { 310 | return $this->geos()->distance($geometry->geos()); 311 | } 312 | } 313 | 314 | public function hausdorffDistance($geometry) { 315 | if ($this->geos()) { 316 | return $this->geos()->hausdorffDistance($geometry->geos()); 317 | } 318 | } 319 | 320 | 321 | // Public - Placeholders 322 | // --------------------- 323 | public function hasZ() { 324 | // geoPHP does not support Z values at the moment 325 | return FALSE; 326 | } 327 | 328 | public function is3D() { 329 | // geoPHP does not support 3D geometries at the moment 330 | return FALSE; 331 | } 332 | 333 | public function isMeasured() { 334 | // geoPHP does not yet support M values 335 | return FALSE; 336 | } 337 | 338 | public function isEmpty() { 339 | // geoPHP does not yet support empty geometries 340 | return FALSE; 341 | } 342 | 343 | public function coordinateDimension() { 344 | // geoPHP only supports 2-dimentional space 345 | return 2; 346 | } 347 | 348 | public function z() { 349 | // geoPHP only supports 2-dimentional space 350 | return NULL; 351 | } 352 | 353 | public function m() { 354 | // geoPHP only supports 2-dimentional space 355 | return NULL; 356 | } 357 | 358 | } 359 | -------------------------------------------------------------------------------- /geophp/lib/geometry/GeometryCollection.class.php: -------------------------------------------------------------------------------- 1 | 4 | * (c) Patrick Hayes 5 | * 6 | * This code is open-source and licenced under the Modified BSD License. 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | /** 12 | * GeometryCollection : a GeometryCollection geometry. 13 | * 14 | * @package sfMapFishPlugin 15 | * @subpackage GeoJSON 16 | * @author Camptocamp 17 | * @version 18 | */ 19 | class GeometryCollection extends Collection 20 | { 21 | protected $geom_type = 'GeometryCollection'; 22 | 23 | /** 24 | * Constructor 25 | * 26 | * @param array $geometries The Geometries array 27 | */ 28 | public function __construct(array $geometries = null) { 29 | parent::__construct($geometries); 30 | } 31 | 32 | /** 33 | * Returns an array suitable for serialization 34 | * 35 | * Overrides the one defined in parent class 36 | * 37 | * @return array 38 | */ 39 | public function getGeoInterface() { 40 | $geometries = array(); 41 | foreach ($this->components as $geometry) { 42 | $geometries[] = $geometry->getGeoInterface(); 43 | } 44 | return array( 45 | 'type' => $this->getGeomType(), 46 | 'geometries' => $geometries 47 | ); 48 | } 49 | 50 | // Not valid for this geomettry 51 | public function boundary() { return NULL; } 52 | public function isSimple() { return NULL; } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /geophp/lib/geometry/LineString.class.php: -------------------------------------------------------------------------------- 1 | 4 | * (c) Patrick Hayes 5 | * 6 | * This code is open-source and licenced under the Modified BSD License. 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | /** 12 | * LineString : a LineString geometry. 13 | * 14 | * @package sfMapFishPlugin 15 | * @subpackage GeoJSON 16 | * @author Camptocamp 17 | * @version 18 | */ 19 | class LineString extends Collection 20 | { 21 | protected $geom_type = 'LineString'; 22 | 23 | /** 24 | * Constructor 25 | * 26 | * @param array $positions The Point array 27 | */ 28 | public function __construct(array $positions) { 29 | if (count($positions) > 1) 30 | { 31 | parent::__construct($positions); 32 | } 33 | else 34 | { 35 | throw new Exception("Linestring with less than two points"); 36 | } 37 | } 38 | 39 | // The boundary of a linestring is itself 40 | public function boundary() { 41 | return $this; 42 | } 43 | 44 | public function startPoint() { 45 | return $this->pointN(1); 46 | } 47 | 48 | public function endPoint() { 49 | $last_n = $this->numPoints(); 50 | return $this->pointN($last_n); 51 | } 52 | 53 | public function isClosed() { 54 | //@@TODO: Need to complete equal() first; 55 | #return ($this->startPoint->equal($this->endPoint())); 56 | } 57 | 58 | public function isRing() { 59 | //@@TODO: need to complete isSimple first 60 | #return ($this->isClosed() && $this->isSimple()); 61 | } 62 | 63 | public function numPoints() { 64 | return $this->numGeometries(); 65 | } 66 | 67 | public function pointN($n) { 68 | return $this->geometryN($n); 69 | } 70 | 71 | public function dimension() { 72 | return 1; 73 | } 74 | 75 | public function area() { 76 | return 0; 77 | } 78 | 79 | } 80 | 81 | -------------------------------------------------------------------------------- /geophp/lib/geometry/MultiLineString.class.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | /** 10 | * MultiLineString : a MultiLineString geometry. 11 | * 12 | * @package sfMapFishPlugin 13 | * @subpackage GeoJSON 14 | * @author Camptocamp 15 | * @version 16 | */ 17 | class MultiLineString extends Collection 18 | { 19 | protected $geom_type = 'MultiLineString'; 20 | 21 | /** 22 | * Constructor 23 | * 24 | * @param array $linestrings The LineString array 25 | */ 26 | public function __construct(array $linestrings) { 27 | parent::__construct($linestrings); 28 | } 29 | 30 | // Length of a MultiLineString is the sum of it's components 31 | public function length() { 32 | if ($this->geos()) { 33 | return $this->geos()->length(); 34 | } 35 | 36 | $length = 0; 37 | foreach ($this->components as $line) { 38 | $length += $line->length(); 39 | } 40 | return $length; 41 | } 42 | 43 | // MultiLineString is closed if all it's components are closed 44 | public function isClosed() { 45 | foreach ($this->components as $line) { 46 | if (!$line->isClosed()) { 47 | return FALSE; 48 | } 49 | } 50 | return TRUE; 51 | } 52 | 53 | } 54 | 55 | -------------------------------------------------------------------------------- /geophp/lib/geometry/MultiPoint.class.php: -------------------------------------------------------------------------------- 1 | 4 | * (c) Patrick Hayes 5 | * 6 | * This code is open-source and licenced under the Modified BSD License. 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | /** 12 | * MultiPoint : a MultiPoint geometry. 13 | * 14 | * @package sfMapFishPlugin 15 | * @subpackage GeoJSON 16 | * @author Camptocamp 17 | * @version 18 | */ 19 | class MultiPoint extends Collection 20 | { 21 | protected $geom_type = 'MultiPoint'; 22 | 23 | /** 24 | * Constructor 25 | * 26 | * @param array $points The Point array 27 | */ 28 | public function __construct(array $points) { 29 | parent::__construct($points); 30 | } 31 | 32 | } 33 | 34 | -------------------------------------------------------------------------------- /geophp/lib/geometry/MultiPolygon.class.php: -------------------------------------------------------------------------------- 1 | 4 | * (c) Patrick Hayes 5 | * 6 | * This code is open-source and licenced under the Modified BSD License. 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | /** 12 | * MultiPolygon : a MultiPolygon geometry. 13 | * 14 | * @package sfMapFishPlugin 15 | * @subpackage GeoJSON 16 | * @author Camptocamp 17 | * @version 18 | */ 19 | class MultiPolygon extends Collection 20 | { 21 | protected $geom_type = 'MultiPolygon'; 22 | 23 | /** 24 | * Constructor 25 | * 26 | * @param array $polygons The Polygon array 27 | */ 28 | public function __construct(array $polygons) { 29 | parent::__construct($polygons); 30 | } 31 | 32 | } 33 | 34 | -------------------------------------------------------------------------------- /geophp/lib/geometry/Point.class.php: -------------------------------------------------------------------------------- 1 | 4 | * (c) Patrick Hayes 5 | * 6 | * This code is open-source and licenced under the Modified BSD License. 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | /** 12 | * Point : a Point geometry. 13 | * 14 | */ 15 | class Point extends Geometry 16 | { 17 | private $position = array(2); 18 | protected $geom_type = 'Point'; 19 | 20 | /** 21 | * Constructor 22 | * 23 | * @param float $x The x coordinate (or longitude) 24 | * @param float $y The y coordinate (or latitude) 25 | */ 26 | public function __construct($x, $y) { 27 | if (!is_numeric($x) || !is_numeric($y)) { 28 | throw new Exception("Bad coordinates: x and y should be numeric"); 29 | } 30 | 31 | // Convert to floatval in case they are passed in as a string or integer etc. 32 | $x = floatval($x); 33 | $y = floatval($y); 34 | 35 | $this->position = array($x, $y); 36 | } 37 | 38 | /** 39 | * An accessor method which returns the coordinates array 40 | * 41 | * @return array The coordinates array 42 | */ 43 | public function getCoordinates() { 44 | return $this->position; 45 | } 46 | 47 | /** 48 | * Returns X coordinate of the point 49 | * 50 | * @return integer The X coordinate 51 | */ 52 | public function x() { 53 | return $this->position[0]; 54 | } 55 | 56 | /** 57 | * Returns Y coordinate of the point 58 | * 59 | * @return integer The Y coordinate 60 | */ 61 | public function y() { 62 | return $this->position[1]; 63 | } 64 | 65 | // A point's centroid is itself 66 | public function centroid() { 67 | return $this; 68 | } 69 | 70 | public function getBBox() { 71 | return array( 72 | 'maxy' => $this->getY(), 73 | 'miny' => $this->getY(), 74 | 'maxx' => $this->getX(), 75 | 'minx' => $this->getX(), 76 | ); 77 | } 78 | 79 | public function area() { 80 | return 0; 81 | } 82 | 83 | public function length() { 84 | return 0; 85 | } 86 | 87 | // The bounadry of a point is itself 88 | public function boundary() { 89 | return $this; 90 | } 91 | 92 | public function dimension() { 93 | return 0; 94 | } 95 | 96 | // Not valid for this geometry type 97 | public function numGeometries() { return NULL; } 98 | public function geometryN($n) { return NULL; } 99 | public function startPoint() { return NULL; } 100 | public function endPoint() { return NULL; } 101 | public function isRing() { return NULL; } 102 | public function isClosed() { return NULL; } 103 | public function numPoints() { return NULL; } 104 | public function pointN($n) { return NULL; } 105 | public function exteriorRing() { return NULL; } 106 | public function numInteriorRings() { return NULL; } 107 | public function interiorRingN($n) { return NULL; } 108 | public function pointOnSurface() { return NULL; } 109 | 110 | } 111 | 112 | -------------------------------------------------------------------------------- /geophp/lib/geometry/Polygon.class.php: -------------------------------------------------------------------------------- 1 | 4 | * (c) Patrick Hayes 5 | * 6 | * This code is open-source and licenced under the Modified BSD License. 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | /** 12 | * Polygon : a Polygon geometry. 13 | * 14 | */ 15 | class Polygon extends Collection 16 | { 17 | protected $geom_type = 'Polygon'; 18 | 19 | /** 20 | * Constructor 21 | * 22 | * The first linestring is the outer ring 23 | * The subsequent ones are holes 24 | * All linestrings should be a closed LineString 25 | * 26 | * @param array $linestrings The LineString array 27 | */ 28 | public function __construct(array $linestrings) { 29 | if (count($linestrings) > 0) { 30 | parent::__construct($linestrings); 31 | } 32 | else { 33 | throw new Exception("Polygon without an exterior ring"); 34 | } 35 | } 36 | 37 | public function area($exterior_only = FALSE, $signed = FALSE) { 38 | if ($this->geos() && $exterior_only == FALSE) { 39 | return $this->geos()->area(); 40 | } 41 | 42 | $exterior_ring = $this->components[0]; 43 | $pts = $exterior_ring->getComponents(); 44 | 45 | $c = count($pts); 46 | if((int)$c == '0') return NULL; 47 | $a = '0'; 48 | foreach($pts as $k => $p){ 49 | $j = ($k + 1) % $c; 50 | $a = $a + ($p->getX() * $pts[$j]->getY()) - ($p->getY() * $pts[$j]->getX()); 51 | } 52 | 53 | if ($signed) $area = ($a / 2); 54 | else $area = abs(($a / 2)); 55 | 56 | if ($exterior_only == TRUE) { 57 | return $area; 58 | } 59 | foreach ($this->components as $delta => $component) { 60 | if ($delta != 0) { 61 | $inner_poly = new Polygon(array($component)); 62 | $area -= $inner_poly->area(); 63 | } 64 | } 65 | return $area; 66 | } 67 | 68 | public function centroid() { 69 | if ($this->geos()) { 70 | return geoPHP::geosToGeometry($this->geos()->centroid()); 71 | } 72 | 73 | $exterior_ring = $this->components[0]; 74 | $pts = $exterior_ring->getComponents(); 75 | 76 | $c = count($pts); 77 | if((int)$c == '0') return NULL; 78 | $cn = array('x' => '0', 'y' => '0'); 79 | $a = $this->area(TRUE, TRUE); 80 | 81 | // If this is a polygon with no area. Just return the first point. 82 | if ($a == 0) { 83 | return $this->exteriorRing()->pointN(1); 84 | } 85 | 86 | foreach($pts as $k => $p){ 87 | $j = ($k + 1) % $c; 88 | $P = ($p->getX() * $pts[$j]->getY()) - ($p->getY() * $pts[$j]->getX()); 89 | $cn['x'] = $cn['x'] + ($p->getX() + $pts[$j]->getX()) * $P; 90 | $cn['y'] = $cn['y'] + ($p->getY() + $pts[$j]->getY()) * $P; 91 | } 92 | 93 | $cn['x'] = $cn['x'] / ( 6 * $a); 94 | $cn['y'] = $cn['y'] / ( 6 * $a); 95 | 96 | $centroid = new Point($cn['x'], $cn['y']); 97 | return $centroid; 98 | } 99 | 100 | public function exteriorRing() { 101 | return $this->components[0]; 102 | } 103 | 104 | public function numInteriorRings() { 105 | return $this->numGeometries()-1; 106 | } 107 | 108 | public function interiorRingN($n) { 109 | return $this->geometryN($n+1); 110 | } 111 | 112 | public function dimension() { 113 | return 2; 114 | } 115 | 116 | // Not valid for this geometry type 117 | // -------------------------------- 118 | public function length() { return NULL; } 119 | 120 | } 121 | 122 | -------------------------------------------------------------------------------- /geophp/tests/input/barret_spur.gpx: -------------------------------------------------------------------------------- 1 | 2 | 1374Vista Ridge TrailheadTrail Head 3 | 1777Wy'East Basin 4 | 1823Dollar Lake 5 | 2394Barrett SpurSummit 6 | 7 | Barrett Spur 1 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | Barrett Spur 2 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /geophp/tests/input/big_n_ugly.kml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | KML Samples 5 | 1 6 | Unleash your creativity with the help of these examples! 7 | 14 | 24 | 33 | 42 | 48 | 56 | 64 | 72 | 80 | 88 | 96 | 105 | 106 | Placemarks 107 | These are just some of the different kinds of placemarks with 108 | which you can mark your favorite places 109 | 110 | -122.0839597145766 111 | 37.42222904525232 112 | 0 113 | -148.4122922628044 114 | 40.5575073395506 115 | 500.6566641072245 116 | 117 | 118 | Simple placemark 119 | Attached to the ground. Intelligently places itself at the 120 | height of the underlying terrain. 121 | 122 | -122.0822035425683,37.42228990140251,0 123 | 124 | 125 | 126 | Floating placemark 127 | 0 128 | Floats a defined distance above the ground. 129 | 130 | -122.0839597145766 131 | 37.42222904525232 132 | 0 133 | -148.4122922628044 134 | 40.5575073395506 135 | 500.6566641072245 136 | 137 | #downArrowIcon 138 | 139 | relativeToGround 140 | -122.084075,37.4220033612141,50 141 | 142 | 143 | 144 | Extruded placemark 145 | 0 146 | Tethered to the ground by a customizable 147 | "tail" 148 | 149 | -122.0845787421525 150 | 37.42215078737763 151 | 0 152 | -148.4126684946234 153 | 40.55750733918048 154 | 365.2646606980322 155 | 156 | #globeIcon 157 | 158 | 1 159 | relativeToGround 160 | -122.0857667006183,37.42156927867553,50 161 | 162 | 163 | 164 | 165 | Styles and Markup 166 | 0 167 | With KML it is easy to create rich, descriptive markup to 168 | annotate and enrich your placemarks 169 | 170 | -122.0845787422371 171 | 37.42215078726837 172 | 0 173 | -148.4126777488172 174 | 40.55750733930874 175 | 365.2646826292919 176 | 177 | #noDrivingDirections 178 | 179 | Highlighted Icon 180 | 0 181 | Place your mouse over the icon to see it display the new 182 | icon 183 | 184 | -122.0856552124024 185 | 37.4224281311035 186 | 0 187 | 0 188 | 0 189 | 265.8520424250024 190 | 191 | 198 | 205 | 206 | 207 | normal 208 | #normalPlacemark 209 | 210 | 211 | highlight 212 | #highlightPlacemark 213 | 214 | 215 | 216 | Roll over this icon 217 | 0 218 | #exampleStyleMap 219 | 220 | -122.0856545755255,37.42243077405461,0 221 | 222 | 223 | 224 | 225 | Descriptive HTML 226 | 0 227 |
228 | Placemark descriptions can be enriched by using many standard HTML tags.
229 | For example: 230 |
231 | Styles:
232 | Italics, 233 | Bold, 234 | Underlined, 235 | Strike Out, 236 | subscriptsubscript, 237 | superscriptsuperscript, 238 | Big, 239 | Small, 240 | Typewriter, 241 | Emphasized, 242 | Strong, 243 | Code 244 |
245 | Fonts:
246 | red by name, 247 | leaf green by hexadecimal RGB 248 |
249 | size 1, 250 | size 2, 251 | size 3, 252 | size 4, 253 | size 5, 254 | size 6, 255 | size 7 256 |
257 | Times, 258 | Verdana, 259 | Arial
260 |
261 | Links: 262 |
263 | Google Earth! 264 |
265 | or: Check out our website at www.google.com 266 |
267 | Alignment:
268 |

left

269 |

center

270 |

right

271 |
272 | Ordered Lists:
273 |
  1. First
  2. Second
  3. Third
274 |
  1. First
  2. Second
  3. Third
275 |
  1. First
  2. Second
  3. Third
276 |
277 | Unordered Lists:
278 |
  • A
  • B
  • C
279 |
  • A
  • B
  • C
280 |
  • A
  • B
  • C
281 |
282 | Definitions:
283 |
284 |
Google:
The best thing since sliced bread
285 |
286 |
287 | Centered:
288 | Time present and time past
289 | Are both perhaps present in time future,
290 | And time future contained in time past.
291 | If all time is eternally present
292 | All time is unredeemable.
293 |
294 |
295 | Block Quote: 296 |
297 |
298 | We shall not cease from exploration
299 | And the end of all our exploring
300 | Will be to arrive where we started
301 | And know the place for the first time.
302 | -- T.S. Eliot 303 |
304 |
305 |
306 | Headings:
307 |

Header 1

308 |

Header 2

309 |

Header 3

310 |

Header 4

311 |

Header 5

312 |
313 | Images:
314 | Remote image
315 |
316 | Scaled image
317 |
318 |
319 | Simple Tables:
320 | 321 | 322 | 323 |
12345
abcde
324 |
325 | [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.]]]>
326 |
327 |
328 | 329 | Ground Overlays 330 | 0 331 | Examples of ground overlays 332 | 333 | Large-scale overlay on terrain 334 | 0 335 | Overlay shows Mount Etna erupting on July 13th, 2001. 336 | 337 | 15.02468937557116 338 | 37.67395167941667 339 | 0 340 | -16.5581842842829 341 | 58.31228652890705 342 | 30350.36838438907 343 | 344 | 345 | http://code.google.com/apis/kml/documentation/etna.jpg 346 | 347 | 348 | 37.91904192681665 349 | 37.46543388598137 350 | 15.35832653742206 351 | 14.60128369746704 352 | -0.1556640799496235 353 | 354 | 355 | 356 | 357 | Screen Overlays 358 | 0 359 | Screen overlays have to be authored directly in KML. These 360 | examples illustrate absolute and dynamic positioning in screen space. 361 | 362 | Simple crosshairs 363 | 0 364 | This screen overlay uses fractional positioning to put the 365 | image in the exact center of the screen 366 | 367 | http://code.google.com/apis/kml/documentation/crosshairs.png 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | Absolute Positioning: Top left 376 | 0 377 | 378 | http://code.google.com/apis/kml/documentation/top_left.jpg 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | Absolute Positioning: Top right 387 | 0 388 | 389 | http://code.google.com/apis/kml/documentation/top_right.jpg 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | Absolute Positioning: Bottom left 398 | 0 399 | 400 | http://code.google.com/apis/kml/documentation/bottom_left.jpg 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | Absolute Positioning: Bottom right 409 | 0 410 | 411 | http://code.google.com/apis/kml/documentation/bottom_right.jpg 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | Dynamic Positioning: Top of screen 420 | 0 421 | 422 | http://code.google.com/apis/kml/documentation/dynamic_screenoverlay.jpg 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | Dynamic Positioning: Right of screen 431 | 0 432 | 433 | http://code.google.com/apis/kml/documentation/dynamic_right.jpg 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | Paths 443 | 0 444 | Examples of paths. Note that the tessellate tag is by default 445 | set to 0. If you want to create tessellated lines, they must be authored 446 | (or edited) directly in KML. 447 | 448 | Tessellated 449 | 0 450 | tag has a value of 1, the line will contour to the underlying terrain]]> 451 | 452 | -112.0822680013139 453 | 36.09825589333556 454 | 0 455 | 103.8120432044965 456 | 62.04855796276328 457 | 2889.145007690472 458 | 459 | 460 | 1 461 | -112.0814237830345,36.10677870477137,0 462 | -112.0870267752693,36.0905099328766,0 463 | 464 | 465 | 466 | Untessellated 467 | 0 468 | tag has a value of 0, the line follow a simple straight-line path from point to point]]> 469 | 470 | -112.0822680013139 471 | 36.09825589333556 472 | 0 473 | 103.8120432044965 474 | 62.04855796276328 475 | 2889.145007690472 476 | 477 | 478 | 0 479 | -112.080622229595,36.10673460007995,0 480 | -112.085242575315,36.09049598612422,0 481 | 482 | 483 | 484 | Absolute 485 | 0 486 | Transparent purple line 487 | 488 | -112.2719329043177 489 | 36.08890633450894 490 | 0 491 | -106.8161545998597 492 | 44.60763714063257 493 | 2569.386744398339 494 | 495 | #transPurpleLineGreenPoly 496 | 497 | 1 498 | absolute 499 | -112.265654928602,36.09447672602546,2357 500 | -112.2660384528238,36.09342608838671,2357 501 | -112.2668139013453,36.09251058776881,2357 502 | -112.2677826834445,36.09189827357996,2357 503 | -112.2688557510952,36.0913137941187,2357 504 | -112.2694810717219,36.0903677207521,2357 505 | -112.2695268555611,36.08932171487285,2357 506 | -112.2690144567276,36.08850916060472,2357 507 | -112.2681528815339,36.08753813597956,2357 508 | -112.2670588176031,36.08682685262568,2357 509 | -112.2657374587321,36.08646312301303,2357 510 | 511 | 512 | 513 | Absolute Extruded 514 | 0 515 | Transparent green wall with yellow outlines 516 | 517 | -112.2643334742529 518 | 36.08563154742419 519 | 0 520 | -125.7518698668815 521 | 44.61038665812578 522 | 4451.842204068102 523 | 524 | #yellowLineGreenPoly 525 | 526 | 1 527 | 1 528 | absolute 529 | -112.2550785337791,36.07954952145647,2357 530 | -112.2549277039738,36.08117083492122,2357 531 | -112.2552505069063,36.08260761307279,2357 532 | -112.2564540158376,36.08395660588506,2357 533 | -112.2580238976449,36.08511401044813,2357 534 | -112.2595218489022,36.08584355239394,2357 535 | -112.2608216347552,36.08612634548589,2357 536 | -112.262073428656,36.08626019085147,2357 537 | -112.2633204928495,36.08621519860091,2357 538 | -112.2644963846444,36.08627897945274,2357 539 | -112.2656969554589,36.08649599090644,2357 540 | 541 | 542 | 543 | Relative 544 | 0 545 | Black line (10 pixels wide), height tracks terrain 546 | 547 | -112.2580438551384 548 | 36.1072674824385 549 | 0 550 | 4.947421249553717 551 | 44.61324882043339 552 | 2927.61105910266 553 | 554 | #thickBlackLine 555 | 556 | 1 557 | relativeToGround 558 | -112.2532845153347,36.09886943729116,645 559 | -112.2540466121145,36.09919570465255,645 560 | -112.254734666947,36.09984998366178,645 561 | -112.255493345654,36.10051310621746,645 562 | -112.2563157098468,36.10108441943419,645 563 | -112.2568033076439,36.10159722088088,645 564 | -112.257494011321,36.10204323542867,645 565 | -112.2584106072308,36.10229131995655,645 566 | -112.2596588987972,36.10240001286358,645 567 | -112.2610581199487,36.10213176873407,645 568 | -112.2626285262793,36.10157011437219,645 569 | 570 | 571 | 572 | Relative Extruded 573 | 0 574 | Opaque blue walls with red outline, height tracks terrain 575 | 576 | -112.2683594333433 577 | 36.09884362144909 578 | 0 579 | -72.24271551768405 580 | 44.60855445139561 581 | 2184.193522571467 582 | 583 | #redLineBluePoly 584 | 585 | 1 586 | 1 587 | relativeToGround 588 | -112.2656634181359,36.09445214722695,630 589 | -112.2652238941097,36.09520916122063,630 590 | -112.2645079986395,36.09580763864907,630 591 | -112.2638827428817,36.09628572284063,630 592 | -112.2635746835406,36.09679275951239,630 593 | -112.2635711822407,36.09740038871899,630 594 | -112.2640296531825,36.09804913435539,630 595 | -112.264327720538,36.09880337400301,630 596 | -112.2642436562271,36.09963644790288,630 597 | -112.2639148687042,36.10055381117246,630 598 | -112.2626894973474,36.10149062823369,630 599 | 600 | 601 | 602 | 603 | Polygons 604 | 0 605 | Examples of polygon shapes 606 | 607 | Google Campus 608 | 0 609 | A collection showing how easy it is to create 3-dimensional 610 | buildings 611 | 612 | -122.084120030116 613 | 37.42174011925477 614 | 0 615 | -34.82469740081282 616 | 53.454348562403 617 | 276.7870053764046 618 | 619 | 620 | Building 40 621 | 0 622 | #transRedPoly 623 | 624 | 1 625 | relativeToGround 626 | 627 | 628 | -122.0848938459612,37.42257124044786,17 629 | -122.0849580979198,37.42211922626856,17 630 | -122.0847469573047,37.42207183952619,17 631 | -122.0845725380962,37.42209006729676,17 632 | -122.0845954886723,37.42215932700895,17 633 | -122.0838521118269,37.42227278564371,17 634 | -122.083792243335,37.42203539112084,17 635 | -122.0835076656616,37.42209006957106,17 636 | -122.0834709464152,37.42200987395161,17 637 | -122.0831221085748,37.4221046494946,17 638 | -122.0829247374572,37.42226503990386,17 639 | -122.0829339169385,37.42231242843094,17 640 | -122.0833837359737,37.42225046087618,17 641 | -122.0833607854248,37.42234159228745,17 642 | -122.0834204551642,37.42237075460644,17 643 | -122.083659133885,37.42251292011001,17 644 | -122.0839758438952,37.42265873093781,17 645 | -122.0842374743331,37.42265143972521,17 646 | -122.0845036949503,37.4226514386435,17 647 | -122.0848020460801,37.42261133916315,17 648 | -122.0847882750515,37.42256395055121,17 649 | -122.0848938459612,37.42257124044786,17 650 | 651 | 652 | 653 | 654 | 655 | Building 41 656 | 0 657 | #transBluePoly 658 | 659 | 1 660 | relativeToGround 661 | 662 | 663 | -122.0857412771483,37.42227033155257,17 664 | -122.0858169768481,37.42231408832346,17 665 | -122.085852582875,37.42230337469744,17 666 | -122.0858799945639,37.42225686138789,17 667 | -122.0858860101409,37.4222311076138,17 668 | -122.0858069157288,37.42220250173855,17 669 | -122.0858379542653,37.42214027058678,17 670 | -122.0856732640519,37.42208690214408,17 671 | -122.0856022926407,37.42214885429042,17 672 | -122.0855902778436,37.422128290487,17 673 | -122.0855841672237,37.42208171967246,17 674 | -122.0854852065741,37.42210455874995,17 675 | -122.0855067264352,37.42214267949824,17 676 | -122.0854430712915,37.42212783846172,17 677 | -122.0850990714904,37.42251282407603,17 678 | -122.0856769818632,37.42281815323651,17 679 | -122.0860162273783,37.42244918858722,17 680 | -122.0857260327004,37.42229239604253,17 681 | -122.0857412771483,37.42227033155257,17 682 | 683 | 684 | 685 | 686 | 687 | Building 42 688 | 0 689 | #transGreenPoly 690 | 691 | 1 692 | relativeToGround 693 | 694 | 695 | -122.0857862287242,37.42136208886969,25 696 | -122.0857312990603,37.42136935989481,25 697 | -122.0857312992918,37.42140934910903,25 698 | -122.0856077073679,37.42138390166565,25 699 | -122.0855802426516,37.42137299550869,25 700 | -122.0852186221971,37.42137299504316,25 701 | -122.0852277765639,37.42161656508265,25 702 | -122.0852598189347,37.42160565894403,25 703 | -122.0852598185499,37.42168200156,25 704 | -122.0852369311478,37.42170017860346,25 705 | -122.0852643957828,37.42176197982575,25 706 | -122.0853239032746,37.42176198013907,25 707 | -122.0853559454324,37.421852864452,25 708 | -122.0854108752463,37.42188921823734,25 709 | -122.0854795379357,37.42189285337048,25 710 | -122.0855436229819,37.42188921797546,25 711 | -122.0856260178042,37.42186013499926,25 712 | -122.085937287963,37.42186013453605,25 713 | -122.0859428718666,37.42160898590042,25 714 | -122.0859655469861,37.42157992759144,25 715 | -122.0858640462341,37.42147115002957,25 716 | -122.0858548911215,37.42140571326184,25 717 | -122.0858091162768,37.4214057134039,25 718 | -122.0857862287242,37.42136208886969,25 719 | 720 | 721 | 722 | 723 | 724 | Building 43 725 | 0 726 | #transYellowPoly 727 | 728 | 1 729 | relativeToGround 730 | 731 | 732 | -122.0844371128284,37.42177253003091,19 733 | -122.0845118855746,37.42191111542896,19 734 | -122.0850470999805,37.42178755121535,19 735 | -122.0850719913391,37.42143663023161,19 736 | -122.084916406232,37.42137237822116,19 737 | -122.0842193868167,37.42137237801626,19 738 | -122.08421938659,37.42147617161496,19 739 | -122.0838086419991,37.4214613409357,19 740 | -122.0837899728564,37.42131306410796,19 741 | -122.0832796534698,37.42129328840593,19 742 | -122.0832609819207,37.42139213944298,19 743 | -122.0829373621737,37.42137236399876,19 744 | -122.0829062425667,37.42151569778871,19 745 | -122.0828502269665,37.42176282576465,19 746 | -122.0829435788635,37.42176776969635,19 747 | -122.083217411188,37.42179248552686,19 748 | -122.0835970430103,37.4217480074456,19 749 | -122.0839455556771,37.42169364237603,19 750 | -122.0840077894637,37.42176283815853,19 751 | -122.084113587521,37.42174801104392,19 752 | -122.0840762473784,37.42171341292375,19 753 | -122.0841447047739,37.42167881534569,19 754 | -122.084144704223,37.42181720660197,19 755 | -122.0842503333074,37.4218170700446,19 756 | -122.0844371128284,37.42177253003091,19 757 | 758 | 759 | 760 | 761 | 762 | 763 | Extruded Polygon 764 | A simple way to model a building 765 | 766 | The Pentagon 767 | 768 | -77.05580139178142 769 | 38.870832443487 770 | 59.88865561738225 771 | 48.09646074797388 772 | 742.0552506670548 773 | 774 | 775 | 1 776 | relativeToGround 777 | 778 | 779 | -77.05788457660967,38.87253259892824,100 780 | -77.05465973756702,38.87291016281703,100 781 | -77.05315536854791,38.87053267794386,100 782 | -77.05552622493516,38.868757801256,100 783 | -77.05844056290393,38.86996206506943,100 784 | -77.05788457660967,38.87253259892824,100 785 | 786 | 787 | 788 | 789 | -77.05668055019126,38.87154239798456,100 790 | -77.05542625960818,38.87167890344077,100 791 | -77.05485125901024,38.87076535397792,100 792 | -77.05577677433152,38.87008686581446,100 793 | -77.05691162017543,38.87054446963351,100 794 | -77.05668055019126,38.87154239798456,100 795 | 796 | 797 | 798 | 799 | 800 | 801 | Absolute and Relative 802 | 0 803 | Four structures whose roofs meet exactly. Turn on/off 804 | terrain to see the difference between relative and absolute 805 | positioning. 806 | 807 | -112.3348969157552 808 | 36.14845533214919 809 | 0 810 | -86.91235037566909 811 | 49.30695423894192 812 | 990.6761201087104 813 | 814 | 815 | Absolute 816 | 0 817 | #transBluePoly 818 | 819 | 1 820 | absolute 821 | 822 | 823 | -112.3382510731295,36.14888505105318,1784 824 | -112.3356128688403,36.14781540589019,1784 825 | -112.3368169371048,36.14658677734382,1784 826 | -112.3374408457543,36.14762778914076,1784 827 | -112.3382510731295,36.14888505105318,1784 828 | 829 | 830 | 831 | 832 | 833 | Absolute Extruded 834 | 0 835 | #transRedPoly 836 | 837 | 1 838 | 1 839 | absolute 840 | 841 | 842 | -112.3396586818843,36.14637618647505,1784 843 | -112.3380597654315,36.14531751871353,1784 844 | -112.3368254237788,36.14659596244607,1784 845 | -112.3384555043203,36.14762621763982,1784 846 | -112.3396586818843,36.14637618647505,1784 847 | 848 | 849 | 850 | 851 | 852 | Relative 853 | 0 854 | 855 | -112.3350152490417 856 | 36.14943123077423 857 | 0 858 | -118.9214100848499 859 | 37.92486261093203 860 | 345.5169113679813 861 | 862 | #transGreenPoly 863 | 864 | 1 865 | relativeToGround 866 | 867 | 868 | -112.3349463145932,36.14988705767721,100 869 | -112.3354019540677,36.14941108398372,100 870 | -112.3344428289146,36.14878490381308,100 871 | -112.3331289492913,36.14780840132443,100 872 | -112.3317019516947,36.14680755678357,100 873 | -112.331131440106,36.1474173426228,100 874 | -112.332616324338,36.14845453364654,100 875 | -112.3339876620524,36.14926570522069,100 876 | -112.3349463145932,36.14988705767721,100 877 | 878 | 879 | 880 | 881 | 882 | Relative Extruded 883 | 0 884 | 885 | -112.3351587892382 886 | 36.14979247129029 887 | 0 888 | -55.42811560891606 889 | 56.10280503739589 890 | 401.0997279712519 891 | 892 | #transYellowPoly 893 | 894 | 1 895 | 1 896 | relativeToGround 897 | 898 | 899 | -112.3348783983763,36.1514008468736,100 900 | -112.3372535345629,36.14888517553886,100 901 | -112.3356068927954,36.14781612679284,100 902 | -112.3350034807972,36.14846469024177,100 903 | -112.3358353861232,36.1489624162954,100 904 | -112.3345888301373,36.15026229372507,100 905 | -112.3337937856278,36.14978096026463,100 906 | -112.3331798208424,36.1504472788618,100 907 | -112.3348783983763,36.1514008468736,100 908 | 909 | 910 | 911 | 912 | 913 | 914 |
915 |
916 | -------------------------------------------------------------------------------- /geophp/tests/input/box.georss: -------------------------------------------------------------------------------- 1 | 42.943 -71.032 43.039 -69.856 -------------------------------------------------------------------------------- /geophp/tests/input/cdata.kml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CDATA example 6 | 7 | CDATA Tags are useful! 9 |

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

12 | ]]> 13 |
14 | 15 | 102.595626,14.996729 16 | 17 |
18 |
19 |
20 | -------------------------------------------------------------------------------- /geophp/tests/input/circle.georss: -------------------------------------------------------------------------------- 1 | 42.943 -71.032 500 -------------------------------------------------------------------------------- /geophp/tests/input/fells_loop.gpx: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 44.586548 12 | 13 | 5066 14 | 15 | Crossing 16 | 17 | 18 | 19 | 57.607200 20 | 21 | 5067 22 | 23 | Dot 24 | 25 | 26 | 27 | 44.826904 28 | 29 | 5096 30 | 31 | Dot 32 | 33 | 34 | 35 | 50.594727 36 | 37 | 5142 38 | 39 | Dot 40 | 41 | 42 | 43 | 127.711200 44 | 45 | 5156 46 | 47 | Dot 48 | 49 | 50 | 51 | 96.926400 52 | 53 | 5224 54 | 55 | Dot 56 | 57 | 58 | 59 | 82.600800 60 | 61 | 5229 62 | 63 | Dot 64 | 65 | 66 | 67 | 82.905600 68 | 69 | 5237 70 | 71 | Dot 72 | 73 | 74 | 75 | 66.696655 76 | 77 | 5254 78 | 79 | Dot 80 | 81 | 82 | 83 | 74.627442 84 | 85 | 5258 86 | 87 | Dot 88 | 89 | 90 | 91 | 65.254761 92 | 93 | 5264 94 | 95 | Dot 96 | 97 | 98 | 99 | 77.419200 100 | 101 | 526708 102 | 103 | Dot 104 | 105 | 106 | 107 | 74.676000 108 | 109 | 526750 110 | 111 | Dot 112 | 113 | 114 | 115 | 78.713135 116 | 117 | 527614 118 | 119 | Dot 120 | 121 | 122 | 123 | 78.713135 124 | 125 | 527631 126 | 127 | Dot 128 | 129 | 130 | 131 | 68.275200 132 | 133 | 5278 134 | 135 | Dot 136 | 137 | 138 | 139 | 64.008000 140 | 141 | 5289 142 | 143 | Dot 144 | 145 | 146 | 147 | 52.997925 148 | 149 | 5374FIRE 150 | 151 | Dot 152 | 153 | 154 | 155 | 56.388000 156 | 157 | 5376 158 | 159 | Dot 160 | 161 | 162 | 163 | 56.388000 164 | 165 | 6006 166 | 167 | Dot 168 | 169 | 170 | 171 | 46.028564 172 | 173 | 6006BLUE 174 | 175 | Dot 176 | 177 | 178 | 179 | 37.616943 180 | 181 | 6014MEADOW 182 | 183 | Dot 184 | 185 | 186 | 187 | 56.388000 188 | 189 | 6029 190 | 191 | Dot 192 | 193 | 194 | 195 | 50.292000 196 | 197 | 6053 198 | 199 | Dot 200 | 201 | 202 | 203 | 25.603200 204 | 205 | 6066 206 | 207 | Dot 208 | 209 | 210 | 211 | 34.442400 212 | 213 | 6067 214 | 215 | Dot 216 | 217 | 218 | 219 | 30.480000 220 | 221 | 6071 222 | 223 | Dot 224 | 225 | 226 | 227 | 15.240000 228 | 229 | 6073 230 | 231 | Dot 232 | 233 | 234 | 235 | 37.795200 236 | 237 | 6084 238 | 239 | Dot 240 | 241 | 242 | 243 | 64.008000 244 | 245 | 6130 246 | 247 | Dot 248 | 249 | 250 | 251 | 64.008000 252 | 253 | 6131 254 | 255 | Dot 256 | 257 | 258 | 259 | 62.788800 260 | 261 | 6153 262 | 263 | Dot 264 | 265 | 266 | 267 | 55.473600 268 | 269 | 6171 270 | 271 | Dot 272 | 273 | 274 | 275 | 62.484000 276 | 277 | 6176 278 | 279 | Dot 280 | 281 | 282 | 283 | 62.179200 284 | 285 | 6177 286 | 287 | Dot 288 | 289 | 290 | 291 | 69.799200 292 | 293 | 6272 294 | 295 | Dot 296 | 297 | 298 | 299 | 73.152000 300 | 301 | 6272 302 | 303 | Dot 304 | 305 | 306 | 307 | 70.104000 308 | 309 | 6278 310 | 311 | Dot 312 | 313 | 314 | 315 | 57.564209 316 | 317 | 6280 318 | 319 | Dot 320 | 321 | 322 | 323 | 66.696655 324 | 325 | 6283 326 | 327 | Dot 328 | 329 | 330 | 331 | 72.945191 332 | 333 | 6289 334 | 335 | Dot 336 | 337 | 338 | 339 | 72.847200 340 | 341 | 6297 342 | 343 | Dot 344 | 345 | 346 | 347 | 53.644800 348 | 349 | 6328 350 | 351 | Dot 352 | 353 | 354 | 355 | 43.891200 356 | 357 | 6354 358 | 359 | Dot 360 | 361 | 362 | 363 | 48.768000 364 | 365 | 635722 366 | 367 | Dot 368 | 369 | 370 | 371 | 49.072800 372 | 373 | 635783 374 | 375 | Dot 376 | 377 | 378 | 379 | 62.484000 380 | 381 | 6373 382 | 383 | Dot 384 | 385 | 386 | 387 | 3.962400 388 | 389 | 6634 390 | 391 | Dot 392 | 393 | 394 | 395 | 13.411200 396 | 397 | 6979 398 | 399 | Dot 400 | 401 | 402 | 403 | 34.012085 404 | 405 | 6997 406 | 407 | Dot 408 | 409 | 410 | 411 | 87.782400 412 | 413 | BEAR HILL 414 | BEAR HILL TOWER 415 | 416 | Tall Tower 417 | 418 | 419 | 420 | 23.469600 421 | 422 | BELLEVUE 423 | BELLEVUE 424 | 425 | Parking Area 426 | 427 | 428 | 429 | 43.384766 430 | 431 | 6016 432 | 433 | Trailhead 434 | 435 | 436 | 437 | 89.916000 438 | 439 | 5236BRIDGE 440 | 441 | Bridge 442 | 443 | 444 | 445 | 55.473600 446 | 447 | 5376BRIDGE 448 | 449 | Bridge 450 | 451 | 452 | 453 | 52.730400 454 | 455 | 6181CROSS 456 | 457 | Crossing 458 | 459 | 460 | 461 | 45.110400 462 | 463 | 6042CROSS 464 | 465 | Crossing 466 | 467 | 468 | 469 | DARKHOLLPO 470 | 471 | Fishing Area 472 | 473 | 474 | 56.083200 475 | 476 | 6121DEAD 477 | 478 | Danger Area 479 | 480 | 481 | 482 | 117.043200 483 | 484 | 5179DEAD 485 | 486 | Danger Area 487 | 488 | 489 | 490 | 69.494400 491 | 492 | 5299DEAD 493 | 494 | Danger Area 495 | 496 | 497 | 498 | 56.997600 499 | 500 | 5376DEAD 501 | 502 | Danger Area 503 | 504 | 505 | 506 | 46.939200 507 | 508 | 6353DEAD 509 | 510 | Danger Area 511 | 512 | 513 | 514 | 61.264800 515 | 516 | 6155DEAD 517 | 518 | Danger Area 519 | 520 | 521 | 522 | 110.947200 523 | 524 | GATE14 525 | 526 | Truck Stop 527 | 528 | 529 | 530 | 77.724000 531 | 532 | GATE16 533 | 534 | Truck Stop 535 | 536 | 537 | 538 | 65.836800 539 | 540 | GATE17 541 | 542 | Truck Stop 543 | 544 | 545 | 546 | 57.302400 547 | 548 | GATE19 549 | 550 | Truck Stop 551 | 552 | 553 | 554 | 49.377600 555 | 556 | GATE21 557 | 558 | Truck Stop 559 | 560 | 561 | 562 | 81.076800 563 | 564 | GATE24 565 | 566 | Truck Stop 567 | 568 | 569 | 570 | 21.515015 571 | 572 | GATE5 573 | 574 | Truck Stop 575 | 576 | 577 | 578 | 26.561890 579 | 580 | GATE6 581 | 582 | Trailhead 583 | 584 | 585 | 586 | 32.004000 587 | 588 | 6077LOGS 589 | 590 | Amusement Park 591 | 592 | 593 | 594 | 119.809082 595 | 596 | 5148NANEPA 597 | 598 | Trailhead 599 | 600 | 601 | 602 | 73.761600 603 | 604 | 5267OBSTAC 605 | 606 | Amusement Park 607 | 608 | 609 | 610 | 45.307495 611 | 612 | PANTHRCAVE 613 | 614 | Tunnel 615 | 616 | 617 | 618 | 77.992066 619 | 620 | 5252PURPLE 621 | 622 | Summit 623 | 624 | 625 | 626 | 67.970400 627 | 628 | 5287WATER 629 | 630 | Swimming Area 631 | 632 | 633 | 634 | 81.076800 635 | 636 | 5239ROAD 637 | 638 | Truck Stop 639 | 640 | 641 | 642 | 67.360800 643 | 644 | 5278ROAD 645 | 646 | Truck Stop 647 | 648 | 649 | 650 | 53.949600 651 | 652 | 5058ROAD 653 | ROAD CROSSING 654 | 655 | Dot 656 | 657 | 658 | 659 | 69.799200 660 | 661 | SHEEPFOLD 662 | 663 | Parking Area 664 | 665 | 666 | 667 | 64.008000 668 | 669 | SOAPBOX 670 | 671 | Cemetery 672 | 673 | 674 | 675 | 64.533692 676 | 677 | 5376STREAM 678 | 679 | Bridge 680 | 681 | 682 | 683 | 61.649902 684 | 685 | 5144SUMMIT 686 | 687 | Summit 688 | 689 | 690 | 691 | 67.360800 692 | 693 | 5150TANK 694 | WATER TANK 695 | 696 | Museum 697 | 698 | 699 | 700 | BELLEVUE 701 | 702 | 1 703 | 704 | 23.469600 705 | 706 | BELLEVUE 707 | BELLEVUE 708 | 709 | Parking Area 710 | 711 | 712 | 713 | 26.561890 714 | 715 | GATE6 716 | 717 | Trailhead 718 | 719 | 720 | 721 | 45.307495 722 | 723 | PANTHRCAVE 724 | 725 | Tunnel 726 | 727 | 728 | 729 | 37.616943 730 | 731 | 6014MEADOW 732 | 733 | Dot 734 | 735 | 736 | 737 | 56.388000 738 | 739 | 6006 740 | 741 | Dot 742 | 743 | 744 | 745 | 46.028564 746 | 747 | 6006BLUE 748 | 749 | Dot 750 | 751 | 752 | 753 | 44.826904 754 | 755 | 5096 756 | 757 | Dot 758 | 759 | 760 | 761 | 44.586548 762 | 763 | 5066 764 | 765 | Crossing 766 | 767 | 768 | 769 | 57.607200 770 | 771 | 5067 772 | 773 | Dot 774 | 775 | 776 | 777 | 53.949600 778 | 779 | 5058ROAD 780 | ROAD CROSSING 781 | 782 | Dot 783 | 784 | 785 | 786 | 67.360800 787 | 788 | 5150TANK 789 | WATER TANK 790 | 791 | Museum 792 | 793 | 794 | 795 | 50.594727 796 | 797 | 5142 798 | 799 | Dot 800 | 801 | 802 | 803 | 61.649902 804 | 805 | 5144SUMMIT 806 | 807 | Summit 808 | 809 | 810 | 811 | 127.711200 812 | 813 | 5156 814 | 815 | Dot 816 | 817 | 818 | 819 | 119.809082 820 | 821 | 5148NANEPA 822 | 823 | Trailhead 824 | 825 | 826 | 827 | 74.627442 828 | 829 | 5258 830 | 831 | Dot 832 | 833 | 834 | 835 | 77.992066 836 | 837 | 5252PURPLE 838 | 839 | Summit 840 | 841 | 842 | 843 | 78.713135 844 | 845 | 527631 846 | 847 | Dot 848 | 849 | 850 | 851 | 78.713135 852 | 853 | 527614 854 | 855 | Dot 856 | 857 | 858 | 859 | 73.761600 860 | 861 | 5267OBSTAC 862 | 863 | Amusement Park 864 | 865 | 866 | 867 | 68.275200 868 | 869 | 5278 870 | 871 | Dot 872 | 873 | 874 | 875 | 64.008000 876 | 877 | 5289 878 | 879 | Dot 880 | 881 | 882 | 883 | 52.997925 884 | 885 | 5374FIRE 886 | 887 | Dot 888 | 889 | 890 | 891 | 56.388000 892 | 893 | 5376 894 | 895 | Dot 896 | 897 | 898 | 899 | 64.533692 900 | 901 | 5376STREAM 902 | 903 | Bridge 904 | 905 | 906 | 907 | 53.644800 908 | 909 | 6328 910 | 911 | Dot 912 | 913 | 914 | 915 | 48.768000 916 | 917 | 635722 918 | 919 | Dot 920 | 921 | 922 | 923 | 49.072800 924 | 925 | 635783 926 | 927 | Dot 928 | 929 | 930 | 931 | 62.484000 932 | 933 | 6373 934 | 935 | Dot 936 | 937 | 938 | 939 | 87.782400 940 | 941 | BEAR HILL 942 | BEAR HILL TOWER 943 | 944 | Tall Tower 945 | 946 | 947 | 948 | 72.945191 949 | 950 | 6289 951 | 952 | Dot 953 | 954 | 955 | 956 | 72.847200 957 | 958 | 6297 959 | 960 | Dot 961 | 962 | 963 | 964 | 66.696655 965 | 966 | 6283 967 | 968 | Dot 969 | 970 | 971 | 972 | 57.564209 973 | 974 | 6280 975 | 976 | Dot 977 | 978 | 979 | 980 | 62.179200 981 | 982 | 6177 983 | 984 | Dot 985 | 986 | 987 | 988 | 62.484000 989 | 990 | 6176 991 | 992 | Dot 993 | 994 | 995 | 996 | 62.788800 997 | 998 | 6153 999 | 1000 | Dot 1001 | 1002 | 1003 | 1004 | 55.473600 1005 | 1006 | 6171 1007 | 1008 | Dot 1009 | 1010 | 1011 | 1012 | 64.008000 1013 | 1014 | 6131 1015 | 1016 | Dot 1017 | 1018 | 1019 | 1020 | 64.008000 1021 | 1022 | 6130 1023 | 1024 | Dot 1025 | 1026 | 1027 | 1028 | 56.388000 1029 | 1030 | 6029 1031 | 1032 | Dot 1033 | 1034 | 1035 | 1036 | 56.388000 1037 | 1038 | 6006 1039 | 1040 | Dot 1041 | 1042 | 1043 | 1044 | 37.616943 1045 | 1046 | 6014MEADOW 1047 | 1048 | Dot 1049 | 1050 | 1051 | 1052 | 45.307495 1053 | 1054 | PANTHRCAVE 1055 | 1056 | Tunnel 1057 | 1058 | 1059 | 1060 | 26.561890 1061 | 1062 | GATE6 1063 | 1064 | Trailhead 1065 | 1066 | 1067 | 1068 | 23.469600 1069 | 1070 | BELLEVUE 1071 | BELLEVUE 1072 | 1073 | Parking Area 1074 | 1075 | 1076 | 1077 | -------------------------------------------------------------------------------- /geophp/tests/input/geometrycollection.georss: -------------------------------------------------------------------------------- 1 | 2 | 4 | Earthquakes 5 | International earthquake observation labs 6 | 7 | 2005-12-13T18:30:02Z 8 | 9 | Dr. Thaddeus Remor 10 | tremor@quakelab.edu 11 | 12 | urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 13 | 14 | This has multiple georss entries in it 15 | 16 | urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a 17 | 2005-08-17T07:02:32Z 18 | We just had a big one. 19 | 45.256 -71.92 20 | 42.256 -69.92 21 | 45.256 -110.45 46.46 -109.48 43.84 -109.86 22 | 45.256 -110.45 46.46 -109.48 43.84 -109.86 45.256 -110.45 23 | 24 | 25 | Another Entryd 26 | Another point. 27 | 46.256 -70.92 28 | 29 | -------------------------------------------------------------------------------- /geophp/tests/input/geometrycollection.wkt: -------------------------------------------------------------------------------- 1 | GEOMETRYCOLLECTION (POINT (4 6), LINESTRING (4 6,7 10)) 2 | -------------------------------------------------------------------------------- /geophp/tests/input/line.georss: -------------------------------------------------------------------------------- 1 | 45.256 -110.45 46.46 -109.48 43.84 -109.86 -------------------------------------------------------------------------------- /geophp/tests/input/linestring.wkt: -------------------------------------------------------------------------------- 1 | LINESTRING (30 10, 10 30, 40 40) 2 | -------------------------------------------------------------------------------- /geophp/tests/input/multilinestring.wkt: -------------------------------------------------------------------------------- 1 | MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10)) 2 | -------------------------------------------------------------------------------- /geophp/tests/input/multipolygon.wkb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fillerwriter/PHP5-Shape-File-Parser/18873626ec95961e9d882eb5b356b62b5b2dea77/geophp/tests/input/multipolygon.wkb -------------------------------------------------------------------------------- /geophp/tests/input/multipolygon.wkt: -------------------------------------------------------------------------------- 1 | MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5))) 2 | -------------------------------------------------------------------------------- /geophp/tests/input/path.kml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Paths 5 | Examples of paths. Note that the tessellate tag is by default 6 | set to 0. If you want to create tessellated lines, they must be authored 7 | (or edited) directly in KML. 8 | 17 | 18 | Absolute Extruded 19 | Transparent green wall with yellow outlines 20 | #yellowLineGreenPoly 21 | 22 | 1 23 | 1 24 | absolute 25 | -112.2550785337791,36.07954952145647,2357 26 | -112.2549277039738,36.08117083492122,2357 27 | -112.2552505069063,36.08260761307279,2357 28 | -112.2564540158376,36.08395660588506,2357 29 | -112.2580238976449,36.08511401044813,2357 30 | -112.2595218489022,36.08584355239394,2357 31 | -112.2608216347552,36.08612634548589,2357 32 | -112.262073428656,36.08626019085147,2357 33 | -112.2633204928495,36.08621519860091,2357 34 | -112.2644963846444,36.08627897945274,2357 35 | -112.2656969554589,36.08649599090644,2357 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /geophp/tests/input/pentagon.kml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The Pentagon 5 | 6 | 1 7 | relativeToGround 8 | 9 | 10 | 11 | -77.05788457660967,38.87253259892824,100 12 | -77.05465973756702,38.87291016281703,100 13 | -77.05315536854791,38.87053267794386,100 14 | -77.05552622493516,38.868757801256,100 15 | -77.05844056290393,38.86996206506943,100 16 | -77.05788457660967,38.87253259892824,100 17 | 18 | 19 | 20 | 21 | 22 | 23 | -77.05668055019126,38.87154239798456,100 24 | -77.05542625960818,38.87167890344077,100 25 | -77.05485125901024,38.87076535397792,100 26 | -77.05577677433152,38.87008686581446,100 27 | -77.05691162017543,38.87054446963351,100 28 | -77.05668055019126,38.87154239798456,100 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /geophp/tests/input/point.georss: -------------------------------------------------------------------------------- 1 | 2 | 4 | Earthquakes 5 | International earthquake observation labs 6 | 7 | 2005-12-13T18:30:02Z 8 | 9 | Dr. Thaddeus Remor 10 | tremor@quakelab.edu 11 | 12 | urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 13 | 14 | M 3.2, Mona Passage 15 | 16 | urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a 17 | 2005-08-17T07:02:32Z 18 | We just had a big one. 19 | 45.256 -71.92 20 | 21 | -------------------------------------------------------------------------------- /geophp/tests/input/point.kml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Simple placemark 5 | Attached to the ground. Intelligently places itself 6 | at the height of the underlying terrain. 7 | 8 | -122.0822035425683,37.42228990140251,0 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /geophp/tests/input/point.wkt: -------------------------------------------------------------------------------- 1 | POINT (10 12) 2 | -------------------------------------------------------------------------------- /geophp/tests/input/polygon.georss: -------------------------------------------------------------------------------- 1 | 2 | 45.256 -110.45 46.46 -109.48 43.84 -109.86 45.256 -110.45 3 | -------------------------------------------------------------------------------- /geophp/tests/input/polygon.wkt: -------------------------------------------------------------------------------- 1 | POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10)) 2 | -------------------------------------------------------------------------------- /geophp/tests/input/polygon2.wkt: -------------------------------------------------------------------------------- 1 | POLYGON ((35 10, 10 20, 15 40, 45 45, 35 10), (20 30, 35 35, 30 20, 20 30)) 2 | -------------------------------------------------------------------------------- /geophp/tests/input/polygon3.wkt: -------------------------------------------------------------------------------- 1 | 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)) 2 | -------------------------------------------------------------------------------- /geophp/tests/input/track.gpx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Garmin International 7 | 8 | 9 | 10 | 11 | Example GPX Document 12 | 13 | 14 | 4.46 15 | 16 | 17 | 18 | 4.94 19 | 20 | 21 | 22 | 6.87 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /geophp/tests/test.php: -------------------------------------------------------------------------------- 1 | area(); 29 | $geometry->boundary(); 30 | $geometry->envelope(); 31 | $geometry->getBBox(); 32 | $geometry->centroid(); 33 | $geometry->length(); 34 | $geometry->y(); 35 | $geometry->x(); 36 | $geometry->numGeometries(); 37 | $geometry->geometryN(1); 38 | $geometry->startPoint(); 39 | $geometry->endPoint(); 40 | $geometry->isRing(); 41 | $geometry->isClosed(); 42 | $geometry->numPoints(); 43 | $geometry->pointN(1); 44 | $geometry->exteriorRing(); 45 | $geometry->numInteriorRings(); 46 | $geometry->interiorRingN(1); 47 | $geometry->dimension(); 48 | $geometry->geometryType(); 49 | $geometry->SRID(); 50 | $geometry->setSRID(4326); 51 | $geometry->getCoordinates(); 52 | $geometry->getGeoInterface(); 53 | 54 | // Aliases 55 | $geometry->getCentroid(); 56 | $geometry->getArea(); 57 | $geometry->getX(); 58 | $geometry->getY(); 59 | $geometry->getGeos(); 60 | $geometry->getGeomType(); 61 | $geometry->getSRID(); 62 | $geometry->asText(); 63 | $geometry->asBinary(); 64 | 65 | // GEOS only functions 66 | $geometry->geos(); 67 | $geometry->setGeos($geometry->geos()); 68 | $geometry->pointOnSurface(); 69 | $geometry->equals($geometry); 70 | $geometry->equalsExact($geometry); 71 | $geometry->relate($geometry); 72 | $geometry->checkValidity(); 73 | $geometry->isSimple(); 74 | $geometry->buffer(10); 75 | $geometry->intersection($geometry); 76 | $geometry->convexHull(); 77 | $geometry->difference($geometry); 78 | $geometry->symDifference($geometry); 79 | $geometry->union($geometry); 80 | $geometry->simplify(0);// @@TODO: Adjust this once we can deal with empty geometries 81 | $geometry->disjoint($geometry); 82 | $geometry->touches($geometry); 83 | $geometry->intersects($geometry); 84 | $geometry->crosses($geometry); 85 | $geometry->within($geometry); 86 | $geometry->contains($geometry); 87 | $geometry->overlaps($geometry); 88 | $geometry->covers($geometry); 89 | $geometry->coveredBy($geometry); 90 | $geometry->distance($geometry); 91 | $geometry->hausdorffDistance($geometry); 92 | 93 | 94 | // Place holders 95 | $geometry->hasZ(); 96 | $geometry->is3D(); 97 | $geometry->isMeasured(); 98 | $geometry->isEmpty(); 99 | $geometry->coordinateDimension(); 100 | $geometry->z(); 101 | $geometry->m(); 102 | 103 | // Test adapter output and input. Do a round-trip and re-test 104 | if ($test_adapters) { 105 | foreach (geoPHP::getAdapterMap() as $adapter_key => $adapter_class) { 106 | if ($adapter_key != 'google_geocode') { //Don't test google geocoder regularily. Uncomment to test 107 | $format = $geometry->out($adapter_key); 108 | $adapter_loader = new $adapter_class(); 109 | $translated_geometry = $adapter_loader->read($format); 110 | #test_geometry($translated_geometry, FALSE); 111 | } 112 | } 113 | } 114 | 115 | } 116 | 117 | 118 | function test_methods($geometry) { 119 | // Cannot test methods if GEOS is not intstalled 120 | if (!geoPHP::geosInstalled()) return; 121 | 122 | $methods = array( 123 | //'boundary', //@@TODO: Uncomment this and fix errors 124 | 'envelope', //@@TODO: Testing reveales errors in this method 125 | 'getBBox', 126 | 'centroid', 127 | 'x', 128 | 'y', 129 | 'startPoint', 130 | 'endPoint', 131 | 'isRing', 132 | 'isClosed', 133 | 'numPoints', 134 | 'getCoordinates', 135 | ); 136 | 137 | foreach ($methods as $method) { 138 | // Turn GEOS on 139 | geoPHP::geosInstalled(TRUE); 140 | $geos_result = $geometry->$method(); 141 | 142 | // Turn GEOS off 143 | geoPHP::geosInstalled(FALSE); 144 | $norm_result = $geometry->$method(); 145 | 146 | // Turn GEOS back On 147 | geoPHP::geosInstalled(TRUE); 148 | 149 | $geos_type = gettype($geos_result); 150 | $norm_type = gettype($norm_result); 151 | 152 | if ($geos_type != $norm_type) { 153 | print 'Type mismatch on '.$method."\n"; 154 | var_dump($geos_type); 155 | var_dump($norm_type); 156 | continue; 157 | } 158 | 159 | // Now check base on type 160 | if ($geos_type == 'object') { 161 | $haus_dist = $geos_result->hausdorffDistance(geoPHP::load($norm_result->out('wkt'),'wkt')); 162 | 163 | // Get the length of the diagonal of the bbox - this is used to scale the haustorff distance 164 | // Using Pythagorean theorem 165 | $bb = $geos_result->getBBox(); 166 | $scale = sqrt((($bb['maxy'] - $bb['miny'])^2) + (($bb['maxx'] - $bb['minx'])^2)); 167 | 168 | // The difference in the output of GEOS and native-PHP methods should be less than 0.5 scaled haustorff units 169 | if ($haus_dist / $scale > 0.5) { 170 | print 'Output mismatch on '.$method.":\n"; 171 | print 'GEOS : '.$geos_result->out('wkt')."\n"; 172 | print 'NORM : '.$norm_result->out('wkt')."\n"; 173 | continue; 174 | } 175 | } 176 | 177 | if ($geos_type == 'boolean' || $geos_type == 'string') { 178 | if ($geos_result !== $norm_result) { 179 | print 'Output mismatch on '.$method.":\n"; 180 | print 'GEOS : '.(string) $geos_result."\n"; 181 | print 'NORM : '.(string) $norm_result."\n"; 182 | continue; 183 | } 184 | } 185 | 186 | //@@TODO: Run tests for output of types arrays and float 187 | //@@TODO: centroid function is non-compliant for collections and strings 188 | } 189 | } 190 | 191 | print "Testing Done!"; -------------------------------------------------------------------------------- /shpParser.php: -------------------------------------------------------------------------------- 1 | shpFilePath = $path; 21 | $this->shpFile = fopen($this->shpFilePath, "rb"); 22 | $this->loadHeaders(); 23 | 24 | $shpData = $this->loadRecords(); 25 | } 26 | 27 | public function headerInfo() { 28 | return $this->headerInfo; 29 | } 30 | 31 | public function getShapeData() { 32 | return $this->shpData; 33 | } 34 | 35 | private function geomTypes() { 36 | return array( 37 | 0 => 'Null Shape', 38 | 1 => 'Point', 39 | 3 => 'PolyLine', 40 | 5 => 'Polygon', 41 | 8 => 'MultiPoint', 42 | 11 => 'PointZ', 43 | 13 => 'PolyLineZ', 44 | 15 => 'PolygonZ', 45 | 18 => 'MultiPointZ', 46 | 21 => 'PointM', 47 | 23 => 'PolyLineM', 48 | 25 => 'PolygonM', 49 | 28 => 'MultiPointM', 50 | 31 => 'MultiPatch', 51 | ); 52 | } 53 | 54 | private function geoTypeFromID($id) { 55 | $geomTypes = $this->geomTypes(); 56 | 57 | if (isset($geomTypes[$id])) { 58 | return $geomTypes[$id]; 59 | } 60 | 61 | return NULL; 62 | } 63 | 64 | private function loadHeaders() { 65 | fseek($this->shpFile, 24, SEEK_SET); 66 | $length = $this->loadData("N"); 67 | fseek($this->shpFile, 32, SEEK_SET); 68 | $shape_type = $this->geoTypeFromID($this->loadData("V")); 69 | 70 | $bounding_box = array(); 71 | $bounding_box["xmin"] = $this->loadData("d"); 72 | $bounding_box["ymin"] = $this->loadData("d"); 73 | $bounding_box["xmax"] = $this->loadData("d"); 74 | $bounding_box["ymax"] = $this->loadData("d"); 75 | 76 | $this->headerInfo = array( 77 | 'length' => $length, 78 | 'shapeType' => array( 79 | 'id' => $shape_type, 80 | 'name' => $this->geoTypeFromID($shape_type), 81 | ), 82 | 'boundingBox' => $bounding_box, 83 | ); 84 | } 85 | 86 | private function loadRecords() { 87 | fseek($this->shpFile, 100); 88 | 89 | while(!feof($this->shpFile)) { 90 | $record = $this->loadRecord(); 91 | if(!empty($record['geom'])){ 92 | $this->shpData[] = $record; 93 | } 94 | } 95 | } 96 | 97 | /** 98 | * Low-level data pull. 99 | * @TODO: extend to enable pulling from shp files directly, or shp files in zip archives. 100 | */ 101 | 102 | private function loadData($type) { 103 | $type_length = $this->loadDataLength($type); 104 | if ($type_length) { 105 | $fread_return = fread($this->shpFile, $type_length); 106 | if ($fread_return != '') { 107 | $tmp = unpack($type, $fread_return); 108 | return current($tmp); 109 | } 110 | } 111 | 112 | return NULL; 113 | } 114 | 115 | private function loadDataLength($type) { 116 | $lengths = array( 117 | 'd' => 8, 118 | 'V' => 4, 119 | 'N' => 4, 120 | ); 121 | 122 | if (isset($lengths[$type])) { 123 | return $lengths[$type]; 124 | } 125 | 126 | return NULL; 127 | } 128 | 129 | // shpRecord functions. 130 | private function loadRecord() { 131 | $recordNumber = $this->loadData("N"); 132 | $this->loadData("N"); // unnecessary data. 133 | $shape_type = $this->loadData("V"); 134 | 135 | $record = array( 136 | 'shapeType' => array( 137 | 'id' => $shape_type, 138 | 'name' => $this->geoTypeFromID($shape_type), 139 | ), 140 | ); 141 | 142 | switch($record['shapeType']['name']){ 143 | case 'Null Shape': 144 | $record['geom'] = $this->loadNullRecord(); 145 | break; 146 | case 'Point': 147 | $record['geom'] = $this->loadPointRecord(); 148 | break; 149 | case 'PolyLine': 150 | $record['geom'] = $this->loadPolyLineRecord(); 151 | break; 152 | case 'Polygon': 153 | $record['geom'] = $this->loadPolygonRecord(); 154 | break; 155 | case 'MultiPoint': 156 | $record['geom'] = $this->loadMultiPointRecord(); 157 | break; 158 | default: 159 | // $setError(sprintf("The Shape Type '%s' is not supported.", $shapeType)); 160 | break; 161 | } 162 | 163 | return $record; 164 | } 165 | 166 | private function loadPoint() { 167 | $data = array(); 168 | $data['x'] = $this->loadData("d"); 169 | $data['y'] = $this->loadData("d"); 170 | return $data; 171 | } 172 | 173 | private function loadNullRecord() { 174 | return array(); 175 | } 176 | 177 | private function loadPolyLineRecord() { 178 | $return = array( 179 | 'bbox' => array( 180 | 'xmin' => $this->loadData("d"), 181 | 'ymin' => $this->loadData("d"), 182 | 'xmax' => $this->loadData("d"), 183 | 'ymax' => $this->loadData("d"), 184 | ), 185 | ); 186 | 187 | $geometries = $this->processLineStrings(); 188 | 189 | $return['numGeometries'] = $geometries['numParts']; 190 | if ($geometries['numParts'] > 1) { 191 | $return['wkt'] = 'MULTILINESTRING(' . implode(', ', $geometries['geometries']) . ')'; 192 | } 193 | else { 194 | $return['wkt'] = 'LINESTRING(' . implode(', ', $geometries['geometries']) . ')'; 195 | } 196 | 197 | return $return; 198 | } 199 | 200 | private function loadPolygonRecord() { 201 | $return = array( 202 | 'bbox' => array( 203 | 'xmin' => $this->loadData("d"), 204 | 'ymin' => $this->loadData("d"), 205 | 'xmax' => $this->loadData("d"), 206 | 'ymax' => $this->loadData("d"), 207 | ), 208 | ); 209 | 210 | $geometries = $this->processLineStrings(); 211 | 212 | $return['numGeometries'] = $geometries['numParts']; 213 | if ($geometries['numParts'] > 1) { 214 | $return['wkt'] = 'MULTIPOLYGON((' . implode('), (', $geometries['geometries']) . '))'; 215 | } 216 | else { 217 | $return['wkt'] = 'POLYGON(' . implode(', ', $geometries['geometries']) . ')'; 218 | } 219 | 220 | return $return; 221 | } 222 | 223 | /** 224 | * Process function for loadPolyLineRecord and loadPolygonRecord. 225 | * Returns geometries array. 226 | */ 227 | 228 | private function processLineStrings() { 229 | $numParts = $this->loadData("V"); 230 | $numPoints = $this->loadData("V"); 231 | $geometries = array(); 232 | 233 | $parts = array(); 234 | for ($i = 0; $i < $numParts; $i++) { 235 | $parts[] = $this->loadData("V"); 236 | } 237 | 238 | $parts[] = $numPoints; 239 | 240 | $points = array(); 241 | for ($i = 0; $i < $numPoints; $i++) { 242 | $points[] = $this->loadPoint(); 243 | } 244 | 245 | if ($numParts == 1) { 246 | for ($i = 0; $i < $numPoints; $i++) { 247 | $geometries[] = sprintf('%f %f', $points[$i]['x'], $points[$i]['y']); 248 | } 249 | 250 | } 251 | else { 252 | for ($i = 0; $i < $numParts; $i++) { 253 | $my_points = array(); 254 | for ($j = $parts[$i]; $j < $parts[$i + 1]; $j++) { 255 | $my_points[] = sprintf('%f %f', $points[$j]['x'], $points[$j]['y']); 256 | } 257 | $geometries[] = '(' . implode(', ', $my_points) . ')'; 258 | } 259 | } 260 | 261 | return array( 262 | 'numParts' => $numParts, 263 | 'geometries' => $geometries, 264 | ); 265 | } 266 | 267 | private function loadMultiPointRecord() { 268 | $return = array( 269 | 'bbox' => array( 270 | 'xmin' => $this->loadData("d"), 271 | 'ymin' => $this->loadData("d"), 272 | 'xmax' => $this->loadData("d"), 273 | 'ymax' => $this->loadData("d"), 274 | ), 275 | 'numGeometries' => $this->loadData("d"), 276 | 'wkt' => '', 277 | ); 278 | 279 | $geometries = array(); 280 | 281 | for ($i = 0; $i < $this->shpData['numGeometries']; $i++) { 282 | $point = $this->loadPoint(); 283 | $geometries[] = sprintf('(%f %f)', $point['x'], $point['y']); 284 | } 285 | 286 | $return['wkt'] = 'MULTIPOINT(' . implode(', ', $geometries) . ')'; 287 | return $return; 288 | } 289 | 290 | private function loadPointRecord() { 291 | $point = $this->loadPoint(); 292 | 293 | $return = array( 294 | 'bbox' => array( 295 | 'xmin' => $point['x'], 296 | 'ymin' => $point['y'], 297 | 'xmax' => $point['x'], 298 | 'ymax' => $point['y'], 299 | ), 300 | 'numGeometries' => 1, 301 | 'wkt' => sprintf('POINT(%f %f)', $point['x'], $point['y']), 302 | ); 303 | 304 | return $return; 305 | } 306 | } 307 | --------------------------------------------------------------------------------