Skip to content
Snippets Groups Projects
Commit cbeb0b30 authored by Nate Hilker's avatar Nate Hilker
Browse files

initial commit

parents
Branches
Tags 0.1.0
No related merge requests found
Showing
with 3555 additions and 0 deletions
/vendor
LICENSE 0 → 100644
Copyright (c) 2011, Patrick Hayes and contributors
This program is dual-licensed under both the GPL version 2 (or later) and
Modified BSD License. Either license may be used at your option.
------------------------------------------------------------------------------
Modified BSD License
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of the contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------------------
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
TODO: proper readme
# Long Story Short
Includes SpatialTools, a static class for generating Geometry from various sources / formats
along with utility methods.
````
$geometry = SpatialTools::load($geojson);
````
Also includes SpatialTrait for Eloquent models.
````
class Office extends Model {
use IANRMedia\SpatialTrait;
public $spatialFields = ['location'];
}
// ...
$point = new IANRMedia\Geometry\Point(-96.668522, 40.828983);
$area = IANRMedia\SpatialTools::pointToCircle($point, 1000);
$offices = Office::within('location', $area)->get();
````
{
"name": "ianrmedia/spatial-tools",
"license": "GPL-2 or New-BSD",
"type": "library",
"description": "mashed up code from phayes/geoPHP, Jelle-S/geoPHP, mammutgroup/laravel-spatial and others for geospatial eloquent models and arcGIS support",
"require": {
"geo-io/wkb-parser": "^1.0"
},
"autoload": {
"psr-0": {
"IANRMedia\\": "src/"
}
},
"authors":[
{
"name":"Nate Hilker"
}
],
"require-dev": {
"phpunit/phpunit": "4.1.*"
}
}
<?php
namespace IANRMedia\SpatialTools\Adapters;
use IANRMedia\SpatialTools\SpatialTools;
use IANRMedia\SpatialTools\Geometry\Geometry;
/**
* EWKB (Extended Well Known Binary) Adapter
*/
class EWKB extends WKB
{
/**
* Read WKB binary string into geometry objects
*
* @param string $wkb An Extended-WKB binary string
*
* @return Geometry
*/
public function read($wkb, $is_hex_string = FALSE) {
if ($is_hex_string) {
$wkb = pack('H*',$wkb);
}
// Open the wkb up in memory so we can examine the SRID
$mem = fopen('php://memory', 'r+');
fwrite($mem, $wkb);
fseek($mem, 0);
$base_info = unpack("corder/ctype/cz/cm/cs", fread($mem, 5));
if ($base_info['s']) {
$srid = current(unpack("Lsrid", fread($mem, 4)));
}
else {
$srid = NULL;
}
fclose($mem);
// Run the wkb through the normal WKB reader to get the geometry
$wkb_reader = new WKB();
$geom = $wkb_reader->read($wkb);
// If there is an SRID, add it to the geometry
if ($srid) {
$geom->setSRID($srid);
}
return $geom;
}
/**
* Serialize geometries into an EWKB binary string.
*
* @param Geometry $geometry
*
* @return string The Extended-WKB binary string representation of the input geometries
*/
public function write(Geometry $geometry, $write_as_hex = FALSE) {
// We always write into NDR (little endian)
$wkb = pack('c',1);
switch ($geometry->getGeomType()) {
case 'Point';
$wkb .= pack('L',1);
$wkb .= $this->writePoint($geometry);
break;
case 'LineString';
$wkb .= pack('L',2);
$wkb .= $this->writeLineString($geometry);
break;
case 'Polygon';
$wkb .= pack('L',3);
$wkb .= $this->writePolygon($geometry);
break;
case 'MultiPoint';
$wkb .= pack('L',4);
$wkb .= $this->writeMulti($geometry);
break;
case 'MultiLineString';
$wkb .= pack('L',5);
$wkb .= $this->writeMulti($geometry);
break;
case 'MultiPolygon';
$wkb .= pack('L',6);
$wkb .= $this->writeMulti($geometry);
break;
case 'GeometryCollection';
$wkb .= pack('L',7);
$wkb .= $this->writeMulti($geometry);
break;
}
if ($write_as_hex) {
$unpacked = unpack('H*',$wkb);
return $unpacked[1];
}
else {
return $wkb;
}
}
}
<?php
namespace IANRMedia\SpatialTools\Adapters;
use IANRMedia\SpatialTools\SpatialTools;
use IANRMedia\SpatialTools\Geometry\Geometry;
/**
* EWKT (Extended Well Known Text) Adapter
*/
class EWKT extends WKT
{
/**
* Serialize geometries into an EWKT string.
*
* @param Geometry $geometry
*
* @return string The Extended-WKT string representation of the input geometries
*/
public function write(Geometry $geometry) {
$srid = $geometry->SRID();
$wkt = '';
if ($srid) {
$wkt = 'SRID=' . $srid . ';';
$wkt .= $geometry->out('wkt');
return $wkt;
}
else {
return $geometry->out('wkt');
}
}
}
<?php
namespace IANRMedia\SpatialTools\Adapters;
use IANRMedia\SpatialTools\SpatialTools;
use IANRMedia\SpatialTools\Geometry\Geometry;
use IANRMedia\SpatialTools\Geometry\GeometryCollection;
use IANRMedia\SpatialTools\Geometry\Point;
use IANRMedia\SpatialTools\Geometry\MultiPoint;
use IANRMedia\SpatialTools\Geometry\Polygon;
use IANRMedia\SpatialTools\Geometry\MultiPolygon;
use IANRMedia\SpatialTools\Geometry\LineString;
use IANRMedia\SpatialTools\Geometry\MultiLineString;
use Exception;
/**
* EsriJSON class : a esrijson/argcis reader/writer.
*
* Note that it will always return a EsriJSON geometry. This
* means that if you pass it a feature, it will return the
* geometry of that feature strip everything else.
*
* Disclaimer: This code is heavily based on the Terraformer ArcGis parser
* javascript code:
* https://github.com/Esri/terraformer-arcgis-parser
*/
class EsriJSON extends GeoAdapter {
/**
* Given an object or a string, return a Geometry
*
* @param mixed $input The EsriJSON string or object
*
* @return object Geometry
*/
public function read($input) {
if (is_string($input)) {
$input = json_decode($input);
}
if (!is_object($input)) {
throw new Exception('Invalid JSON');
}
// TODO: What if the wkid is different from 4326?
//$inputSpatialReference = isset($input->geometry) ? $input->geometry->spatialReference : $input->spatialReference;
if (property_exists($input, 'x') && property_exists($input, 'y')) {
$coords = array($input->x, $input->y);
if (property_exists($input, 'z')) {
$coords[] = $input->z;
}
return $this->arrayToPoint($coords);
}
if (isset($input->points) && $input->points) {
return $this->arrayToMultiPoint($input->points);
}
if (isset($input->paths) && $input->paths) {
if (count($input->paths) === 1) {
return $this->arrayToLineString($input->paths[0]);
}
else {
return $this->arrayToMultiLineString($input->paths);
}
}
if (property_exists($input, 'rings')) {
return $this->convertRingsToGeometry($input->rings);
}
if ((isset($input->compressedGeometry) && $input->compressedGeometry) || (isset($input->geometry)) && $input->geometry) {
if ($input->compressedGeometry) {
$input->geometry = (object) array(
'paths' => array(
$this->decompressGeometry($input->compressedGeometry)
),
);
}
return $this->read($input->geometry);
}
if ((isset($input->features)) && $input->features) {
$geometries = array();
foreach ($input->features as $feature) {
$geometries[] = $this->read($feature);
}
return new GeometryCollection($geometries);
}
// Should have returned something by now.
throw new Exception('Invalid JSON');
}
protected function arrayToPoint($array) {
return new Point(
isset($array[0]) ? $array[0] : NULL,
isset($array[1]) ? $array[1] : NULL,
isset($array[2]) ? $array[2] : NULL
);
}
protected function arrayToMultiPoint($array) {
$points = array();
foreach ($array as $comp_array) {
$points[] = $this->arrayToPoint($comp_array);
}
return new MultiPoint($points);
}
protected function arrayToLineString($array) {
$points = array();
foreach ($array as $comp_array) {
$points[] = $this->arrayToPoint($comp_array);
}
return new LineString($points);
}
protected function arrayToPolygon($array) {
$lines = array();
foreach ($array as $comp_array) {
$lines[] = $this->arrayToLineString($comp_array);
}
return new Polygon($lines);
}
protected function arrayToMultiLineString($array) {
$lines = array();
foreach ($array as $comp_array) {
$lines[] = $this->arrayToLineString($comp_array);
}
return new MultiLineString($lines);
}
protected function arrayToMultiPolygon($array) {
$polys = array();
foreach ($array as $comp_array) {
$polys[] = $this->arrayToPolygon($comp_array);
}
return new MultiPolygon($polys);
}
/**
* Serializes an object into a geojson string
*
*
* @param Geometry $obj The object to serialize
*
* @return string The GeoJSON string
*/
public function write(Geometry $geometry, $return_array = FALSE) {
if ($return_array) {
return $this->getArray($geometry);
}
else {
return json_encode($this->getArray($geometry));
}
}
protected function getArray(Geometry $geometry) {
$result = array('spatialReference' => (object) array('wkid' => 4326));
switch ($geometry->geometryType()) {
case "Point":
$result['x'] = $geometry->x();
$result['y'] = $geometry->y();
if ($geometry->hasZ()) {
$result['z'] = $geometry->z();
}
break;
case "MultiPoint":
$result['points'] = $geometry->asArray();
break;
case "LineString":
$result['paths'] = array($geometry->asArray());
break;
case "MultiLineString":
$result['paths'] = $geometry->asArray();
break;
case "Polygon":
$result['rings'] = $this->orientRings($geometry->asArray());
break;
case "MultiPolygon":
$result['rings'] = $this->flattenMultiPolygonRings($geometry->asArray());
break;
case "GeometryCollection":
$result['features'] = array();
foreach ($geometry->getComponents() as $component) {
$result['features'][] = $this->getArray($component);
}
break;
}
return $result;
}
/**
* Decompresses compressed geometry.
*
*
* @param string $str The compressed geometry.
* @return array The decompressed geometry points.
*/
protected function decompressGeometry($str) {
$xDiffPrev = 0;
$yDiffPrev = 0;
$points = array();
$strings = array();
// Split the string into an array on the + and - characters
$string = preg_match_all('/((\+|\-)[^\+\-]+)/', $str, $strings);
// The first value is the coefficient in base 32
$coefficient = intval($strings[0], 32);
for ($i = 1; $i < count($strings); $i += 2) {
// $i is the offset for the $x value
// Convert the value from base 32 and add the previous $x value
$x = (intval($strings[$i], 32) + $xDiffPrev);
$xDiffPrev = $x;
// j+1 is the offset for the y value
// Convert the value from base 32 and add the previous y value
$y = (intval($strings[i + 1], 32) + $yDiffPrev);
$yDiffPrev = $y;
array_push($points, array($x / $coefficient, $y / $coefficient));
}
return $points;
}
/**
* Checks if the first and last points of a ring are equal and closes the
* ring if not.
*
*
* @param array $coordinates The coordinates of the ring.
* @return array The coordinates of the closed ring.
*/
protected function closeRing($coordinates) {
if (!$this->pointsEqual($coordinates[0], $coordinates[count($coordinates) - 1])) {
array_push($coordinates, $coordinates[0]);
}
return $coordinates;
}
/**
* Checks if 2 x,y points are equal.
*
*
* @param array $a The coordinates of point a.
* @param array $b The coordinates of point b.
* @return boolean Whether or not the points are equal.
*/
protected function pointsEqual($a, $b) {
for ($i = 0; $i < count($a); $i++) {
if ($a[$i] !== $b[$i]) {
return FALSE;
}
}
return TRUE;
}
/**
* Determine if polygon ring coordinates are clockwise. Clockwise signifies
* outer ring, counter-clockwise an inner ring or hole. This logic was found
* at http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-
* of-polygon-points-are-in-clockwise-order
*
*
* @param array $ringToTest The coordinates of the ring to test.
* @return boolean Wether or not the ring is clockwise.
*/
protected function ringIsClockwise($ringToTest) {
$total = 0;
$rLength = count($ringToTest);
$pt1 = $ringToTest[0];
for ($i = 0; $i < $rLength - 1; $i++) {
$pt2 = $ringToTest[$i + 1];
$total += ($pt2[0] - $pt1[0]) * ($pt2[1] + $pt1[1]);
$pt1 = $pt2;
}
return ($total >= 0);
}
/**
* Ensures that rings are oriented in the right directions outer rings are
* clockwise, holes are counterclockwise.
*
*
* @param array $polygon The rings to orient.
* @return array The oriented rings.
*/
protected function orientRings($polygon) {
$output = array();
$outerRing = $this->closeRing(array_shift($polygon));
if (count($outerRing) >= 4) {
if (!$this->ringIsClockwise($outerRing)) {
$outerRing = array_reverse($outerRing);
}
array_push($output, $outerRing);
for ($i = 0; $i < count($polygon); $i++) {
$hole = $this->closeRing($polygon[$i]);
if (count($hole) >= 4) {
if ($this->ringIsClockwise($hole)) {
$hole = array_reverse($hole);
}
array_push($output, $hole);
}
}
}
return $output;
}
/** This function flattens holes in multipolygons to one array of polygons.
* [
* [
* [ array of outer coordinates ]
* [ hole coordinates ]
* [ hole coordinates ]
* ],
* [
* [ array of outer coordinates ]
* [ hole coordinates ]
* [ hole coordinates ]
* ],
* ]
* becomes
* [
* [ array of outer coordinates ]
* [ hole coordinates ]
* [ hole coordinates ]
* [ array of outer coordinates ]
* [ hole coordinates ]
* [ hole coordinates ]
* ]
*
*
* @param array $rings The rings to flatten.
* @return array The flattened rings.
*/
protected function flattenMultiPolygonRings($rings) {
$output = array();
for ($i = 0; $i < count($rings); $i++) {
$polygon = $this->orientRings($rings[$i]);
for ($j = count($polygon) - 1; $j >= 0; $j--) {
array_push($output, $polygon[$j]);
}
}
return $output;
}
/**
* Checks if two edges intersect.
*
*
* @param array $a1 First coordinate of the first edge to check.
* @param array $a2 Second coordinate of the first edge to check.
* @param array $b1 First coordinate of the second edge to check.
* @param array $b2 Second coordinate of the second edge to check.
* @return boolean Whether or not the edges intersect.
*/
protected function edgeIntersectsEdge($a1, $a2, $b1, $b2) {
$ua_t = ($b2[0] - $b1[0]) * ($a1[1] - $b1[1]) - ($b2[1] - $b1[1]) * ($a1[0] - $b1[0]);
$ub_t = ($a2[0] - $a1[0]) * ($a1[1] - $b1[1]) - ($a2[1] - $a1[1]) * ($a1[0] - $b1[0]);
$u_b = ($b2[1] - $b1[1]) * ($a2[0] - $a1[0]) - ($b2[0] - $b1[0]) * ($a2[1] - $a1[1]);
if ($u_b !== 0) {
$ua = $ua_t / $u_b;
$ub = $ub_t / $u_b;
if (0 <= $ua && $ua <= 1 && 0 <= $ub && $ub <= 1) {
return TRUE;
}
}
return FALSE;
}
/**
* Checks if an array of coordinates intersect.
*
* @param array $a The first array of coordinates to check.
* @param array $b The second array of coordinates to check.
* @return boolean Whether or not the arrays of coordinates intersect.
*/
protected function arraysIntersectArrays($a, $b) {
if (is_numeric($a[0][0])) {
if (is_numeric($b[0][0])) {
for ($i = 0; $i < count($a) - 1; $i++) {
for ($j = 0; $j < count($b) - 1; $j++) {
if ($this->edgeIntersectsEdge($a[$i], $a[$i + 1], $b[$j], $b[$j + 1])) {
return TRUE;
}
}
}
}
else {
for ($k = 0; $k < count($b); $k++) {
if ($this->arraysIntersectArrays($a, $b[$k])) {
return TRUE;
}
}
}
}
else {
for ($l = 0; $l < count($a); $l++) {
if ($this->arraysIntersectArrays($a[$l], $b)) {
return TRUE;
}
}
}
return FALSE;
}
/**
* Check if an array of coordinates contain a point.
*
*
* @param array $coordinates The array of coordinates to check.
* @param array $point The coordinates of the point to check.
* @return boolean Whether or not the array of coordinates contain the point.
*/
protected function coordinatesContainPoint($coordinates, $point) {
$contains = FALSE;
for ($i = -1, $l = count($coordinates), $j = $l - 1; ++$i < $l; $j = $i) {
if ((($coordinates[$i][1] <= $point[1] && $point[1] < $coordinates[$j][1]) ||
($coordinates[$j][1] <= $point[1] && $point[1] < $coordinates[$i][1])) &&
($point[0] < ($coordinates[$j][0] - $coordinates[$i][0]) * ($point[1] - $coordinates[$i][1]) / ($coordinates[$j][1] - $coordinates[$i][1]) + $coordinates[$i][0])) {
$contains = !$contains;
}
}
return $contains;
}
/**
* Checks if an array of coordinates contains an other array of coordinates.
*
*
* @param array $outer The array of outer coordinates.
* @param array $inner The array of inner coordinates.
* @return boolean Whether or not the outer array contains the inner array.
*/
protected function coordinatesContainCoordinates($outer, $inner) {
$intersects = $this->arraysIntersectArrays($outer, $inner);
$contains = $this->coordinatesContainPoint($outer, $inner[0]);
if (!$intersects && $contains) {
return TRUE;
}
return FALSE;
}
/**
* Checks if any polygons in this array contain any other polygons in this
* array. Used for checking holes in arcgis rings.
*
*
* @param array $rings The argcis rings to check.
* @return Geometry The converted rings.
*/
protected function convertRingsToGeometry($rings) {
$outerRings = array();
$holes = array();
// For each ring.
for ($r = 0; $r < count($rings); $r++) {
$ring = $this->closeRing($rings[$r]);
if (count($ring) < 4) {
continue;
}
// Is this ring an outer ring? Is it clockwise?
if ($this->ringIsClockwise($ring)) {
$polygon = array($ring);
// Push to outer rings.
array_push($outerRings, $polygon);
}
else {
// Counterclockwise push to holes.
array_push($holes, $ring);
}
}
$uncontainedHoles = array();
// While there are holes left...
while (count($holes)) {
// Pop a hole off out stack.
$hole = array_pop($holes);
// Loop over all outer rings and see if they contain our hole.
$contained = FALSE;
for ($x = count($outerRings) - 1; $x >= 0; $x--) {
$outerRing = $outerRings[$x][0];
if ($this->coordinatesContainCoordinates($outerRing, $hole)) {
// The hole is contained push it into our polygon.
array_push($outerRings[$x], $hole);
$contained = TRUE;
break;
}
}
// The ring is not contained in any outer ring.
// Sometimes this happens https://github.com/Esri/esri-leaflet/issues/320
if (!$contained) {
array_push($uncontainedHoles, $hole);
}
}
// If we couldn't match any holes using contains we can now try intersects...
while (count($uncontainedHoles)) {
// Pop a hole off out stack.
$hole = array_pop($uncontainedHoles);
// Loop over all outer rings and see if any intersect our hole.
$intersects = FALSE;
for ($x = count($outerRings) - 1; $x >= 0; $x--) {
$outerRing = $outerRings[$x][0];
if ($this->arraysIntersectArrays($outerRing, $hole)) {
// The hole intersects the outer ring push it into our polygon.
array_push($outerRings[$x], $hole);
$intersects = TRUE;
break;
}
}
// Hole does not intersect ANY outer ring at this point.
// Make it an outer ring.
if (!$intersects) {
array_push($outerRings, array(array_reverse(hole)));
}
}
if (count($outerRings) === 1) {
return $this->arrayToPolygon($outerRings[0]);
}
else {
return $this->arrayToMultiPolygon($outerRings);
}
}
}
<?php
namespace IANRMedia\SpatialTools\Adapters;
use IANRMedia\SpatialTools\SpatialTools;
use IANRMedia\SpatialTools\Geometry\Geometry;
use IANRMedia\SpatialTools\Geometry\GeometryCollection;
use IANRMedia\SpatialTools\Geometry\Point;
use IANRMedia\SpatialTools\Geometry\MultiPoint;
use IANRMedia\SpatialTools\Geometry\Polygon;
use IANRMedia\SpatialTools\Geometry\MultiPolygon;
use IANRMedia\SpatialTools\Geometry\LineString;
use IANRMedia\SpatialTools\Geometry\MultiLineString;
use Exception;
use InvalidText;
use DOMDocument;
/*
* Copyright (c) Patrick Hayes
*
* This code is open-source and licenced under the Modified BSD License.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* PHP Geometry/GPX encoder/decoder
*/
class GPX extends GeoAdapter
{
private $namespace = FALSE;
private $nss = ''; // Name-space string. eg 'georss:'
/**
* Read GPX string into geometry objects
*
* @param string $gpx A GPX string
*
* @return Geometry|GeometryCollection
*/
public function read($gpx) {
return $this->geomFromText($gpx);
}
/**
* Serialize geometries into a GPX string.
*
* @param Geometry $geometry
*
* @return string The GPX string representation of the input geometries
*/
public function write(Geometry $geometry, $namespace = FALSE) {
if ($geometry->isEmpty()) return NULL;
if ($namespace) {
$this->namespace = $namespace;
$this->nss = $namespace.':';
}
return '<'.$this->nss.'gpx creator="geoPHP" version="1.0">'.$this->geometryToGPX($geometry).'</'.$this->nss.'gpx>';
}
public function geomFromText($text) {
// Change to lower-case and strip all CDATA
$text = strtolower($text);
$text = preg_replace('/<!\[cdata\[(.*?)\]\]>/s','',$text);
// Load into DOMDocument
$xmlobj = new DOMDocument();
@$xmlobj->loadXML($text);
if ($xmlobj === false) {
throw new Exception("Invalid GPX: ". $text);
}
$this->xmlobj = $xmlobj;
try {
$geom = $this->geomFromXML();
} catch(InvalidText $e) {
throw new Exception("Cannot Read Geometry From GPX: ". $text);
} catch(Exception $e) {
throw $e;
}
return $geom;
}
protected function geomFromXML() {
$geometries = array();
$geometries = array_merge($geometries, $this->parseWaypoints());
$geometries = array_merge($geometries, $this->parseTracks());
$geometries = array_merge($geometries, $this->parseRoutes());
if (empty($geometries)) {
throw new Exception("Invalid / Empty GPX");
}
return SpatialTools::geometryReduce($geometries);
}
protected function childElements($xml, $nodename = '') {
$children = array();
foreach ($xml->childNodes as $child) {
if ($child->nodeName == $nodename) {
$children[] = $child;
}
}
return $children;
}
protected function parseWaypoints() {
$points = array();
$wpt_elements = $this->xmlobj->getElementsByTagName('wpt');
foreach ($wpt_elements as $wpt) {
$lat = $wpt->attributes->getNamedItem("lat")->nodeValue;
$lon = $wpt->attributes->getNamedItem("lon")->nodeValue;
$points[] = new Point($lon, $lat);
}
return $points;
}
protected function parseTracks() {
$lines = array();
$trk_elements = $this->xmlobj->getElementsByTagName('trk');
foreach ($trk_elements as $trk) {
$components = array();
foreach ($this->childElements($trk, 'trkseg') as $trkseg) {
foreach ($this->childElements($trkseg, 'trkpt') as $trkpt) {
$lat = $trkpt->attributes->getNamedItem("lat")->nodeValue;
$lon = $trkpt->attributes->getNamedItem("lon")->nodeValue;
$components[] = new Point($lon, $lat);
}
}
if ($components) {$lines[] = new LineString($components);}
}
return $lines;
}
protected function parseRoutes() {
$lines = array();
$rte_elements = $this->xmlobj->getElementsByTagName('rte');
foreach ($rte_elements as $rte) {
$components = array();
foreach ($this->childElements($rte, 'rtept') as $rtept) {
$lat = $rtept->attributes->getNamedItem("lat")->nodeValue;
$lon = $rtept->attributes->getNamedItem("lon")->nodeValue;
$components[] = new Point($lon, $lat);
}
$lines[] = new LineString($components);
}
return $lines;
}
protected function geometryToGPX($geom) {
$type = strtolower($geom->getGeomType());
switch ($type) {
case 'point':
return $this->pointToGPX($geom);
break;
case 'linestring':
return $this->linestringToGPX($geom);
break;
case 'polygon':
case 'multipoint':
case 'multilinestring':
case 'multipolygon':
case 'geometrycollection':
return $this->collectionToGPX($geom);
break;
}
}
private function pointToGPX($geom) {
return '<'.$this->nss.'wpt lat="'.$geom->getY().'" lon="'.$geom->getX().'" />';
}
private function linestringToGPX($geom) {
$gpx = '<'.$this->nss.'trk><'.$this->nss.'trkseg>';
foreach ($geom->getComponents() as $comp) {
$gpx .= '<'.$this->nss.'trkpt lat="'.$comp->getY().'" lon="'.$comp->getX().'" />';
}
$gpx .= '</'.$this->nss.'trkseg></'.$this->nss.'trk>';
return $gpx;
}
public function collectionToGPX($geom) {
$gpx = '';
$components = $geom->getComponents();
foreach ($geom->getComponents() as $comp) {
$gpx .= $this->geometryToGPX($comp);
}
return $gpx;
}
}
<?php
namespace IANRMedia\SpatialTools\Adapters;
use IANRMedia\SpatialTools\SpatialTools;
use IANRMedia\SpatialTools\Geometry\Geometry;
/*
* (c) Patrick Hayes 2011
*
* This code is open-source and licenced under the Modified BSD License.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* GeoAdapter : abstract class which represents an adapter
* for reading and writing to and from Geomtry objects
*
*/
abstract class GeoAdapter
{
/**
* Read input and return a Geometry or GeometryCollection
*
* @return Geometry|GeometryCollection
*/
abstract public function read($input);
/**
* Write out a Geometry or GeometryCollection in the adapter's format
*
* @return mixed
*/
abstract public function write(Geometry $geometry);
}
<?php
namespace IANRMedia\SpatialTools\Adapters;
use IANRMedia\SpatialTools\SpatialTools;
use IANRMedia\SpatialTools\Geometry\Geometry;
use IANRMedia\SpatialTools\Geometry\Point;
use IANRMedia\SpatialTools\Geometry\Polygon;
use IANRMedia\SpatialTools\Geometry\LineString;
/**
* PHP Geometry GeoHash encoder/decoder.
*
* @author prinsmc
* @see http://en.wikipedia.org/wiki/Geohash
*
*/
class GeoHash extends GeoAdapter
{
/**
* base32 encoding character map.
*/
private $table = "0123456789bcdefghjkmnpqrstuvwxyz";
/**
* array of neighbouring hash character maps.
*/
private $neighbours = array (
// north
'top' => array (
'even' => 'p0r21436x8zb9dcf5h7kjnmqesgutwvy',
'odd' => 'bc01fg45238967deuvhjyznpkmstqrwx'
),
// east
'right' => array (
'even' => 'bc01fg45238967deuvhjyznpkmstqrwx',
'odd' => 'p0r21436x8zb9dcf5h7kjnmqesgutwvy'
),
// west
'left' => array (
'even' => '238967debc01fg45kmstqrwxuvhjyznp',
'odd' => '14365h7k9dcfesgujnmqp0r2twvyx8zb'
),
// south
'bottom' => array (
'even' => '14365h7k9dcfesgujnmqp0r2twvyx8zb',
'odd' => '238967debc01fg45kmstqrwxuvhjyznp'
)
);
/**
* array of bordering hash character maps.
*/
private $borders = array (
// north
'top' => array (
'even' => 'prxz',
'odd' => 'bcfguvyz'
),
// east
'right' => array (
'even' => 'bcfguvyz',
'odd' => 'prxz'
),
// west
'left' => array (
'even' => '0145hjnp',
'odd' => '028b'
),
// south
'bottom' => array (
'even' => '028b',
'odd' => '0145hjnp'
)
);
/**
* Convert the geohash to a Point. The point is 2-dimensional.
* @return Point the converted geohash
* @param string $hash a geohash
* @see GeoAdapter::read()
*/
public function read($hash, $as_grid = FALSE) {
$ll = $this->decode($hash);
if (!$as_grid) {
return new Point($ll['medlon'], $ll['medlat']);
}
else {
return new Polygon(array(
new LineString(array(
new Point($ll['minlon'], $ll['maxlat']),
new Point($ll['maxlon'], $ll['maxlat']),
new Point($ll['maxlon'], $ll['minlat']),
new Point($ll['minlon'], $ll['minlat']),
new Point($ll['minlon'], $ll['maxlat']),
))
));
}
}
/**
* Convert the geometry to geohash.
* @return string the geohash or null when the $geometry is not a Point
* @param Point $geometry
* @see GeoAdapter::write()
*/
public function write(Geometry $geometry, $precision = NULL){
if ($geometry->isEmpty()) return '';
if($geometry->geometryType() === 'Point'){
return $this->encodePoint($geometry, $precision);
}
else {
// The geohash is the hash grid ID that fits the envelope
$envelope = $geometry->envelope();
$geohashes = array();
$geohash = '';
foreach ($envelope->getPoints() as $point) {
$geohashes[] = $this->encodePoint($point, 0.0000001);
}
$i = 0;
while ($i < strlen($geohashes[0])) {
$char = $geohashes[0][$i];
foreach ($geohashes as $hash) {
if ($hash[$i] != $char) {
return $geohash;
}
}
$geohash .= $char;
$i++;
}
return $geohash;
}
}
/**
* @return string geohash
* @param Point $point
* @author algorithm based on code by Alexander Songe <a@songe.me>
* @see https://github.com/asonge/php-geohash/issues/1
*/
private function encodePoint($point, $precision = NULL){
if ($precision === NULL) {
$lap = strlen($point->y())-strpos($point->y(),".");
$lop = strlen($point->x())-strpos($point->x(),".");
$precision = pow(10,-max($lap-1,$lop-1,0))/2;
}
$minlat = -90;
$maxlat = 90;
$minlon = -180;
$maxlon = 180;
$latE = 90;
$lonE = 180;
$i = 0;
$error = 180;
$hash='';
while($error>=$precision) {
$chr = 0;
for($b=4;$b>=0;--$b) {
if((1&$b) == (1&$i)) {
// even char, even bit OR odd char, odd bit...a lon
$next = ($minlon+$maxlon)/2;
if($point->x()>$next) {
$chr |= pow(2,$b);
$minlon = $next;
} else {
$maxlon = $next;
}
$lonE /= 2;
} else {
// odd char, even bit OR even char, odd bit...a lat
$next = ($minlat+$maxlat)/2;
if($point->y()>$next) {
$chr |= pow(2,$b);
$minlat = $next;
} else {
$maxlat = $next;
}
$latE /= 2;
}
}
$hash .= $this->table[$chr];
$i++;
$error = min($latE,$lonE);
}
return $hash;
}
/**
* @param string $hash a geohash
* @author algorithm based on code by Alexander Songe <a@songe.me>
* @see https://github.com/asonge/php-geohash/issues/1
*/
private function decode($hash){
$ll = array();
$minlat = -90;
$maxlat = 90;
$minlon = -180;
$maxlon = 180;
$latE = 90;
$lonE = 180;
for($i=0,$c=strlen($hash);$i<$c;$i++) {
$v = strpos($this->table,$hash[$i]);
if(1&$i) {
if(16&$v)$minlat = ($minlat+$maxlat)/2; else $maxlat = ($minlat+$maxlat)/2;
if(8&$v) $minlon = ($minlon+$maxlon)/2; else $maxlon = ($minlon+$maxlon)/2;
if(4&$v) $minlat = ($minlat+$maxlat)/2; else $maxlat = ($minlat+$maxlat)/2;
if(2&$v) $minlon = ($minlon+$maxlon)/2; else $maxlon = ($minlon+$maxlon)/2;
if(1&$v) $minlat = ($minlat+$maxlat)/2; else $maxlat = ($minlat+$maxlat)/2;
$latE /= 8;
$lonE /= 4;
} else {
if(16&$v)$minlon = ($minlon+$maxlon)/2; else $maxlon = ($minlon+$maxlon)/2;
if(8&$v) $minlat = ($minlat+$maxlat)/2; else $maxlat = ($minlat+$maxlat)/2;
if(4&$v) $minlon = ($minlon+$maxlon)/2; else $maxlon = ($minlon+$maxlon)/2;
if(2&$v) $minlat = ($minlat+$maxlat)/2; else $maxlat = ($minlat+$maxlat)/2;
if(1&$v) $minlon = ($minlon+$maxlon)/2; else $maxlon = ($minlon+$maxlon)/2;
$latE /= 4;
$lonE /= 8;
}
}
$ll['minlat'] = $minlat;
$ll['minlon'] = $minlon;
$ll['maxlat'] = $maxlat;
$ll['maxlon'] = $maxlon;
$ll['medlat'] = round(($minlat+$maxlat)/2, max(1, -round(log10($latE)))-1);
$ll['medlon'] = round(($minlon+$maxlon)/2, max(1, -round(log10($lonE)))-1);
return $ll;
}
/**
* Calculates the adjacent geohash of the geohash in the specified direction.
* This algorithm is available in various ports that seem to point back to
* geohash-js by David Troy under MIT notice.
*
*
* @see https://github.com/davetroy/geohash-js
* @see https://github.com/lyokato/objc-geohash
* @see https://github.com/lyokato/libgeohash
* @see https://github.com/masuidrive/pr_geohash
* @see https://github.com/sunng87/node-geohash
* @see https://github.com/davidmoten/geo
*
* @param string $hash the geohash (lowercase)
* @param string $direction the direction of the neighbor (top, bottom, left or right)
* @return string the geohash of the adjacent cell
*/
public function adjacent($hash, $direction){
$last = substr($hash, -1);
$type = (strlen($hash) % 2)? 'odd': 'even';
$base = substr($hash, 0, strlen($hash) - 1);
if(strpos(($this->borders[$direction][$type]), $last) !== false){
$base = $this->adjacent($base, $direction);
}
return $base.$this->table[strpos($this->neighbours[$direction][$type], $last)];
}
}
<?php
namespace IANRMedia\SpatialTools\Adapters;
use IANRMedia\SpatialTools\SpatialTools;
use IANRMedia\SpatialTools\Geometry\Geometry;
use IANRMedia\SpatialTools\Geometry\GeometryCollection;
use IANRMedia\SpatialTools\Geometry\Point;
use IANRMedia\SpatialTools\Geometry\MultiPoint;
use IANRMedia\SpatialTools\Geometry\Polygon;
use IANRMedia\SpatialTools\Geometry\MultiPolygon;
use IANRMedia\SpatialTools\Geometry\LineString;
use IANRMedia\SpatialTools\Geometry\MultiLineString;
use Exception;
/**
* GeoJSON class : a geojson reader/writer.
*
* Note that it will always return a GeoJSON geometry. This
* means that if you pass it a feature, it will return the
* geometry of that feature strip everything else.
*/
class GeoJSON extends GeoAdapter
{
/**
* Given an object or a string, return a Geometry
*
* @param mixed $input The GeoJSON string or object
*
* @return object Geometry
*/
public function read($input) {
if (is_string($input)) {
$input = json_decode($input);
}
if (!is_object($input)) {
throw new Exception('Invalid JSON');
}
if (!is_string($input->type)) {
throw new Exception('Invalid JSON');
}
// Check to see if it's a FeatureCollection
if ($input->type == 'FeatureCollection') {
$geoms = array();
foreach ($input->features as $feature) {
$geoms[] = $this->read($feature);
}
return GeoPHP::geometryReduce($geoms);
}
// Check to see if it's a Feature
if ($input->type == 'Feature') {
return $this->read($input->geometry);
}
// It's a geometry - process it
return $this->objToGeom($input);
}
private function objToGeom($obj) {
$type = $obj->type;
if ($type == 'GeometryCollection') {
return $this->objToGeometryCollection($obj);
}
$method = 'arrayTo' . $type;
return $this->$method($obj->coordinates);
}
private function arrayToPoint($array) {
if (!empty($array)) {
return new Point($array[0], $array[1]);
}
else {
return new Point();
}
}
private function arrayToLineString($array) {
$points = array();
foreach ($array as $comp_array) {
$points[] = $this->arrayToPoint($comp_array);
}
return new LineString($points);
}
private function arrayToPolygon($array) {
$lines = array();
foreach ($array as $comp_array) {
$lines[] = $this->arrayToLineString($comp_array);
}
return new Polygon($lines);
}
private function arrayToMultiPoint($array) {
$points = array();
foreach ($array as $comp_array) {
$points[] = $this->arrayToPoint($comp_array);
}
return new MultiPoint($points);
}
private function arrayToMultiLineString($array) {
$lines = array();
foreach ($array as $comp_array) {
$lines[] = $this->arrayToLineString($comp_array);
}
return new MultiLineString($lines);
}
private function arrayToMultiPolygon($array) {
$polys = array();
foreach ($array as $comp_array) {
$polys[] = $this->arrayToPolygon($comp_array);
}
return new MultiPolygon($polys);
}
private function objToGeometryCollection($obj) {
$geoms = array();
if (empty($obj->geometries)) {
throw new Exception('Invalid GeoJSON: GeometryCollection with no component geometries');
}
foreach ($obj->geometries as $comp_object) {
$geoms[] = $this->objToGeom($comp_object);
}
return new GeometryCollection($geoms);
}
/**
* Serializes an object into a geojson string
*
*
* @param Geometry $obj The object to serialize
*
* @return string The GeoJSON string
*/
public function write(Geometry $geometry, $return_array = FALSE) {
if ($return_array) {
return $this->getArray($geometry);
}
else {
return json_encode($this->getArray($geometry));
}
}
public function getArray($geometry) {
if ($geometry->getGeomType() == 'GeometryCollection') {
$component_array = array();
foreach ($geometry->components as $component) {
$component_array[] = array(
'type' => $component->geometryType(),
'coordinates' => $component->asArray(),
);
}
return array(
'type'=> 'GeometryCollection',
'geometries'=> $component_array,
);
}
else return array(
'type'=> $geometry->getGeomType(),
'coordinates'=> $geometry->asArray(),
);
}
}
<?php
namespace IANRMedia\SpatialTools\Adapters;
use IANRMedia\SpatialTools\SpatialTools;
use IANRMedia\SpatialTools\Geometry\Geometry;
use IANRMedia\SpatialTools\Geometry\GeometryCollection;
use IANRMedia\SpatialTools\Geometry\Point;
use IANRMedia\SpatialTools\Geometry\MultiPoint;
use IANRMedia\SpatialTools\Geometry\Polygon;
use IANRMedia\SpatialTools\Geometry\MultiPolygon;
use IANRMedia\SpatialTools\Geometry\LineString;
use IANRMedia\SpatialTools\Geometry\MultiLineString;
use Exception;
use InvalidText;
use DOMDocument;
/*
* Copyright (c) Patrick Hayes
*
* This code is open-source and licenced under the Modified BSD License.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* PHP Geometry/GeoRSS encoder/decoder
*/
class GeoRSS extends GeoAdapter
{
private $namespace = FALSE;
private $nss = ''; // Name-space string. eg 'georss:'
/**
* Read GeoRSS string into geometry objects
*
* @param string $georss - an XML feed containing geoRSS
*
* @return Geometry|GeometryCollection
*/
public function read($gpx) {
return $this->geomFromText($gpx);
}
/**
* Serialize geometries into a GeoRSS string.
*
* @param Geometry $geometry
*
* @return string The georss string representation of the input geometries
*/
public function write(Geometry $geometry, $namespace = FALSE) {
if ($namespace) {
$this->namespace = $namespace;
$this->nss = $namespace.':';
}
return $this->geometryToGeoRSS($geometry);
}
public function geomFromText($text) {
// Change to lower-case, strip all CDATA, and de-namespace
$text = strtolower($text);
$text = preg_replace('/<!\[cdata\[(.*?)\]\]>/s','',$text);
// Load into DOMDOcument
$xmlobj = new DOMDocument();
@$xmlobj->loadXML($text);
if ($xmlobj === false) {
throw new Exception("Invalid GeoRSS: ". $text);
}
$this->xmlobj = $xmlobj;
try {
$geom = $this->geomFromXML();
} catch(InvalidText $e) {
throw new Exception("Cannot Read Geometry From GeoRSS: ". $text);
} catch(Exception $e) {
throw $e;
}
return $geom;
}
protected function geomFromXML() {
$geometries = array();
$geometries = array_merge($geometries, $this->parsePoints());
$geometries = array_merge($geometries, $this->parseLines());
$geometries = array_merge($geometries, $this->parsePolygons());
$geometries = array_merge($geometries, $this->parseBoxes());
$geometries = array_merge($geometries, $this->parseCircles());
if (empty($geometries)) {
throw new Exception("Invalid / Empty GeoRSS");
}
return SpatialTools::geometryReduce($geometries);
}
protected function getPointsFromCoords($string) {
$coords = array();
if (empty($string)) {
return $coords;
}
$latlon = explode(' ',$string);
foreach ($latlon as $key => $item) {
if (!($key % 2)) {
// It's a latitude
$lat = $item;
}
else {
// It's a longitude
$lon = $item;
$coords[] = new Point($lon, $lat);
}
}
return $coords;
}
protected function parsePoints() {
$points = array();
$pt_elements = $this->xmlobj->getElementsByTagName('point');
foreach ($pt_elements as $pt) {
if ($pt->hasChildNodes()) {
$point_array = $this->getPointsFromCoords(trim($pt->firstChild->nodeValue));
}
if (!empty($point_array)) {
$points[] = $point_array[0];
}
else {
$points[] = new Point();
}
}
return $points;
}
protected function parseLines() {
$lines = array();
$line_elements = $this->xmlobj->getElementsByTagName('line');
foreach ($line_elements as $line) {
$components = $this->getPointsFromCoords(trim($line->firstChild->nodeValue));
$lines[] = new LineString($components);
}
return $lines;
}
protected function parsePolygons() {
$polygons = array();
$poly_elements = $this->xmlobj->getElementsByTagName('polygon');
foreach ($poly_elements as $poly) {
if ($poly->hasChildNodes()) {
$points = $this->getPointsFromCoords(trim($poly->firstChild->nodeValue));
$exterior_ring = new LineString($points);
$polygons[] = new Polygon(array($exterior_ring));
}
else {
// It's an EMPTY polygon
$polygons[] = new Polygon();
}
}
return $polygons;
}
// Boxes are rendered into polygons
protected function parseBoxes() {
$polygons = array();
$box_elements = $this->xmlobj->getElementsByTagName('box');
foreach ($box_elements as $box) {
$parts = explode(' ',trim($box->firstChild->nodeValue));
$components = array(
new Point($parts[3], $parts[2]),
new Point($parts[3], $parts[0]),
new Point($parts[1], $parts[0]),
new Point($parts[1], $parts[2]),
new Point($parts[3], $parts[2]),
);
$exterior_ring = new LineString($components);
$polygons[] = new Polygon(array($exterior_ring));
}
return $polygons;
}
// Circles are rendered into points
// @@TODO: Add good support once we have circular-string geometry support
protected function parseCircles() {
$points = array();
$circle_elements = $this->xmlobj->getElementsByTagName('circle');
foreach ($circle_elements as $circle) {
$parts = explode(' ',trim($circle->firstChild->nodeValue));
$points[] = new Point($parts[1], $parts[0]);
}
return $points;
}
protected function geometryToGeoRSS($geom) {
$type = strtolower($geom->getGeomType());
switch ($type) {
case 'point':
return $this->pointToGeoRSS($geom);
break;
case 'linestring':
return $this->linestringToGeoRSS($geom);
break;
case 'polygon':
return $this->PolygonToGeoRSS($geom);
break;
case 'multipoint':
case 'multilinestring':
case 'multipolygon':
case 'geometrycollection':
return $this->collectionToGeoRSS($geom);
break;
}
return $output;
}
private function pointToGeoRSS($geom) {
$out = '<'.$this->nss.'point>';
if (!$geom->isEmpty()) {
$out .= $geom->getY().' '.$geom->getX();
}
$out .= '</'.$this->nss.'point>';
return $out;
}
private function linestringToGeoRSS($geom) {
$output = '<'.$this->nss.'line>';
foreach ($geom->getComponents() as $k => $point) {
$output .= $point->getY().' '.$point->getX();
if ($k < ($geom->numGeometries() -1)) $output .= ' ';
}
$output .= '</'.$this->nss.'line>';
return $output;
}
private function polygonToGeoRSS($geom) {
$output = '<'.$this->nss.'polygon>';
$exterior_ring = $geom->exteriorRing();
foreach ($exterior_ring->getComponents() as $k => $point) {
$output .= $point->getY().' '.$point->getX();
if ($k < ($exterior_ring->numGeometries() -1)) $output .= ' ';
}
$output .= '</'.$this->nss.'polygon>';
return $output;
}
public function collectionToGeoRSS($geom) {
$georss = '<'.$this->nss.'where>';
$components = $geom->getComponents();
foreach ($geom->getComponents() as $comp) {
$georss .= $this->geometryToGeoRSS($comp);
}
$georss .= '</'.$this->nss.'where>';
return $georss;
}
}
<?php
namespace IANRMedia\SpatialTools\Adapters;
use IANRMedia\SpatialTools\SpatialTools;
use IANRMedia\SpatialTools\Geometry\Geometry;
use IANRMedia\SpatialTools\Geometry\GeometryCollection;
use IANRMedia\SpatialTools\Geometry\Point;
use IANRMedia\SpatialTools\Geometry\MultiPoint;
use IANRMedia\SpatialTools\Geometry\Polygon;
use IANRMedia\SpatialTools\Geometry\MultiPolygon;
use IANRMedia\SpatialTools\Geometry\LineString;
use IANRMedia\SpatialTools\Geometry\MultiLineString;
use Exception;
/*
* (c) Camptocamp <info@camptocamp.com>
* (c) Patrick Hayes
*
* This code is open-source and licenced under the Modified BSD License.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* PHP Google Geocoder Adapter
*
*
* @package geoPHP
* @author Patrick Hayes <patrick.d.hayes@gmail.com>
*/
class GoogleGeocode extends GeoAdapter
{
/**
* Read an address string or array geometry objects
*
* @param string - Address to geocode
* @param string - Type of Geometry to return. Can either be 'points' or 'bounds' (polygon)
* @param Geometry|bounds-array - Limit the search area to within this region. For example
* by default geocoding "Cairo" will return the location of Cairo Egypt.
* If you pass a polygon of illinois, it will return Cairo IL.
* @param return_multiple - Return all results in a multipoint or multipolygon
* @return Geometry|GeometryCollection
*/
public function read($address, $return_type = 'point', $bounds = FALSE, $return_multiple = FALSE) {
if (is_array($address)) $address = join(',', $address);
if (gettype($bounds) == 'object') {
$bounds = $bounds->getBBox();
}
if (gettype($bounds) == 'array') {
$bounds_string = '&bounds='.$bounds['miny'].','.$bounds['minx'].'|'.$bounds['maxy'].','.$bounds['maxx'];
}
else {
$bounds_string = '';
}
$url = "http://maps.googleapis.com/maps/api/geocode/json";
$url .= '?address='. urlencode($address);
$url .= $bounds_string;
$url .= '&sensor=false';
$this->result = json_decode(@file_get_contents($url));
if ($this->result->status == 'OK') {
if ($return_multiple == FALSE) {
if ($return_type == 'point') {
return $this->getPoint();
}
if ($return_type == 'bounds' || $return_type == 'polygon') {
return $this->getPolygon();
}
}
if ($return_multiple == TRUE) {
if ($return_type == 'point') {
$points = array();
foreach ($this->result->results as $delta => $item) {
$points[] = $this->getPoint($delta);
}
return new MultiPoint($points);
}
if ($return_type == 'bounds' || $return_type == 'polygon') {
$polygons = array();
foreach ($this->result->results as $delta => $item) {
$polygons[] = $this->getPolygon($delta);
}
return new MultiPolygon($polygons);
}
}
}
else {
if ($this->result->status) throw new Exception('Error in Google Geocoder: '.$this->result->status);
else throw new Exception('Unknown error in Google Geocoder');
return FALSE;
}
}
/**
* Serialize geometries into a WKT string.
*
* @param Geometry $geometry
* @param string $return_type Should be either 'string' or 'array'
*
* @return string Does a reverse geocode of the geometry
*/
public function write(Geometry $geometry, $return_type = 'string') {
$centroid = $geometry->getCentroid();
$lat = $centroid->getY();
$lon = $centroid->getX();
$url = "http://maps.googleapis.com/maps/api/geocode/json";
$url .= '?latlng='.$lat.','.$lon;
$url .= '&sensor=false';
$this->result = json_decode(@file_get_contents($url));
if ($this->result->status == 'OK') {
if ($return_type == 'string') {
return $this->result->results[0]->formatted_address;
}
if ($return_type == 'array') {
return $this->result->results[0]->address_components;
}
}
elseif ($this->result->status == 'ZERO_RESULTS') {
if ($return_type == 'string') {
return '';
}
if ($return_type == 'array') {
return $this->result->results;
}
}
else {
if ($this->result->status) throw new Exception('Error in Google Reverse Geocoder: '.$this->result->status);
else throw new Exception('Unknown error in Google Reverse Geocoder');
return FALSE;
}
}
private function getPoint($delta = 0) {
$lat = $this->result->results[$delta]->geometry->location->lat;
$lon = $this->result->results[$delta]->geometry->location->lng;
return new Point($lon, $lat);
}
private function getPolygon($delta = 0) {
$points = array (
$this->getTopLeft($delta),
$this->getTopRight($delta),
$this->getBottomRight($delta),
$this->getBottomLeft($delta),
$this->getTopLeft($delta),
);
$outer_ring = new LineString($points);
return new Polygon(array($outer_ring));
}
private function getTopLeft($delta = 0) {
$lat = $this->result->results[$delta]->geometry->bounds->northeast->lat;
$lon = $this->result->results[$delta]->geometry->bounds->southwest->lng;
return new Point($lon, $lat);
}
private function getTopRight($delta = 0) {
$lat = $this->result->results[$delta]->geometry->bounds->northeast->lat;
$lon = $this->result->results[$delta]->geometry->bounds->northeast->lng;
return new Point($lon, $lat);
}
private function getBottomLeft($delta = 0) {
$lat = $this->result->results[$delta]->geometry->bounds->southwest->lat;
$lon = $this->result->results[$delta]->geometry->bounds->southwest->lng;
return new Point($lon, $lat);
}
private function getBottomRight($delta = 0) {
$lat = $this->result->results[$delta]->geometry->bounds->southwest->lat;
$lon = $this->result->results[$delta]->geometry->bounds->northeast->lng;
return new Point($lon, $lat);
}
}
<?php
namespace IANRMedia\SpatialTools\Adapters;
use IANRMedia\SpatialTools\SpatialTools;
use IANRMedia\SpatialTools\Geometry\Geometry;
use IANRMedia\SpatialTools\Geometry\GeometryCollection;
use IANRMedia\SpatialTools\Geometry\Point;
use IANRMedia\SpatialTools\Geometry\MultiPoint;
use IANRMedia\SpatialTools\Geometry\Polygon;
use IANRMedia\SpatialTools\Geometry\MultiPolygon;
use IANRMedia\SpatialTools\Geometry\LineString;
use IANRMedia\SpatialTools\Geometry\MultiLineString;
use Exception;
use InvalidText;
use DOMDocument;
/*
* Copyright (c) Patrick Hayes
* Copyright (c) 2010-2011, Arnaud Renevier
*
* This code is open-source and licenced under the Modified BSD License.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* PHP Geometry/KML encoder/decoder
*
* Mainly inspired/adapted from OpenLayers( http://www.openlayers.org )
* Openlayers/format/WKT.js
*
* @package sfMapFishPlugin
* @subpackage GeoJSON
* @author Camptocamp <info@camptocamp.com>
*/
class KML extends GeoAdapter
{
private $namespace = FALSE;
private $nss = ''; // Name-space string. eg 'georss:'
/**
* Read KML string into geometry objects
*
* @param string $kml A KML string
*
* @return Geometry|GeometryCollection
*/
public function read($kml) {
return $this->geomFromText($kml);
}
/**
* Serialize geometries into a KML string.
*
* @param Geometry $geometry
*
* @return string The KML string representation of the input geometries
*/
public function write(Geometry $geometry, $namespace = FALSE) {
if ($namespace) {
$this->namespace = $namespace;
$this->nss = $namespace.':';
}
return $this->geometryToKML($geometry);
}
public function geomFromText($text) {
// Change to lower-case and strip all CDATA
$text = mb_strtolower($text, mb_detect_encoding($text));
$text = preg_replace('/<!\[cdata\[(.*?)\]\]>/s','',$text);
// Load into DOMDocument
$xmlobj = new DOMDocument();
@$xmlobj->loadXML($text);
if ($xmlobj === false) {
throw new Exception("Invalid KML: ". $text);
}
$this->xmlobj = $xmlobj;
try {
$geom = $this->geomFromXML();
} catch(InvalidText $e) {
throw new Exception("Cannot Read Geometry From KML: ". $text);
} catch(Exception $e) {
throw $e;
}
return $geom;
}
protected function geomFromXML() {
$geometries = array();
$geom_types = SpatialTools::geometryList();
$placemark_elements = $this->xmlobj->getElementsByTagName('placemark');
if ($placemark_elements->length) {
foreach ($placemark_elements as $placemark) {
foreach ($placemark->childNodes as $child) {
// Node names are all the same, except for MultiGeometry, which maps to GeometryCollection
$node_name = $child->nodeName == 'multigeometry' ? 'geometrycollection' : $child->nodeName;
if (array_key_exists($node_name, $geom_types)) {
$function = 'parse'.$geom_types[$node_name];
$geometries[] = $this->$function($child);
}
}
}
}
else {
// The document does not have a placemark, try to create a valid geometry from the root element
$node_name = $this->xmlobj->documentElement->nodeName == 'multigeometry' ? 'geometrycollection' : $this->xmlobj->documentElement->nodeName;
if (array_key_exists($node_name, $geom_types)) {
$function = 'parse'.$geom_types[$node_name];
$geometries[] = $this->$function($this->xmlobj->documentElement);
}
}
return SpatialTools::geometryReduce($geometries);
}
protected function childElements($xml, $nodename = '') {
$children = array();
if ($xml->childNodes) {
foreach ($xml->childNodes as $child) {
if ($child->nodeName == $nodename) {
$children[] = $child;
}
}
}
return $children;
}
protected function parsePoint($xml) {
$coordinates = $this->_extractCoordinates($xml);
if (!empty($coordinates)) {
return new Point($coordinates[0][0],$coordinates[0][1]);
}
else {
return new Point();
}
}
protected function parseLineString($xml) {
$coordinates = $this->_extractCoordinates($xml);
$point_array = array();
foreach ($coordinates as $set) {
$point_array[] = new Point($set[0],$set[1]);
}
return new LineString($point_array);
}
protected function parsePolygon($xml) {
$components = array();
$outer_boundary_element_a = $this->childElements($xml, 'outerboundaryis');
if (empty($outer_boundary_element_a)) {
return new Polygon(); // It's an empty polygon
}
$outer_boundary_element = $outer_boundary_element_a[0];
$outer_ring_element_a = $this->childElements($outer_boundary_element, 'linearring');
$outer_ring_element = $outer_ring_element_a[0];
$components[] = $this->parseLineString($outer_ring_element);
if (count($components) != 1) {
throw new Exception("Invalid KML");
}
$inner_boundary_element_a = $this->childElements($xml, 'innerboundaryis');
if (count($inner_boundary_element_a)) {
foreach ($inner_boundary_element_a as $inner_boundary_element) {
foreach ($this->childElements($inner_boundary_element, 'linearring') as $inner_ring_element) {
$components[] = $this->parseLineString($inner_ring_element);
}
}
}
return new Polygon($components);
}
protected function parseGeometryCollection($xml) {
$components = array();
$geom_types = SpatialTools::geometryList();
foreach ($xml->childNodes as $child) {
$nodeName = ($child->nodeName == 'linearring') ? 'linestring' : $child->nodeName;
if (array_key_exists($nodeName, $geom_types)) {
$function = 'parse'.$geom_types[$nodeName];
$components[] = $this->$function($child);
}
}
return new GeometryCollection($components);
}
protected function _extractCoordinates($xml) {
$coord_elements = $this->childElements($xml, 'coordinates');
$coordinates = array();
if (count($coord_elements)) {
$coord_sets = explode(' ', preg_replace('/[\r\n]+/', ' ', $coord_elements[0]->nodeValue));
foreach ($coord_sets as $set_string) {
$set_string = trim($set_string);
if ($set_string) {
$set_array = explode(',',$set_string);
if (count($set_array) >= 2) {
$coordinates[] = $set_array;
}
}
}
}
return $coordinates;
}
private function geometryToKML($geom) {
$type = strtolower($geom->getGeomType());
switch ($type) {
case 'point':
return $this->pointToKML($geom);
break;
case 'linestring':
return $this->linestringToKML($geom);
break;
case 'polygon':
return $this->polygonToKML($geom);
break;
case 'multipoint':
case 'multilinestring':
case 'multipolygon':
case 'geometrycollection':
return $this->collectionToKML($geom);
break;
}
}
private function pointToKML($geom) {
$out = '<'.$this->nss.'Point>';
if (!$geom->isEmpty()) {
$out .= '<'.$this->nss.'coordinates>'.$geom->getX().",".$geom->getY().'</'.$this->nss.'coordinates>';
}
$out .= '</'.$this->nss.'Point>';
return $out;
}
private function linestringToKML($geom, $type = FALSE) {
if (!$type) {
$type = $geom->getGeomType();
}
$str = '<'.$this->nss . $type .'>';
if (!$geom->isEmpty()) {
$str .= '<'.$this->nss.'coordinates>';
$i=0;
foreach ($geom->getComponents() as $comp) {
if ($i != 0) $str .= ' ';
$str .= $comp->getX() .','. $comp->getY();
$i++;
}
$str .= '</'.$this->nss.'coordinates>';
}
$str .= '</'. $this->nss . $type .'>';
return $str;
}
public function polygonToKML($geom) {
$components = $geom->getComponents();
$str = '';
if (!empty($components)) {
$str = '<'.$this->nss.'outerBoundaryIs>' . $this->linestringToKML($components[0], 'LinearRing') . '</'.$this->nss.'outerBoundaryIs>';
foreach (array_slice($components, 1) as $comp) {
$str .= '<'.$this->nss.'innerBoundaryIs>' . $this->linestringToKML($comp) . '</'.$this->nss.'innerBoundaryIs>';
}
}
return '<'.$this->nss.'Polygon>'. $str .'</'.$this->nss.'Polygon>';
}
public function collectionToKML($geom) {
$components = $geom->getComponents();
$str = '<'.$this->nss.'MultiGeometry>';
foreach ($geom->getComponents() as $comp) {
$sub_adapter = new KML();
$str .= $sub_adapter->write($comp);
}
return $str .'</'.$this->nss.'MultiGeometry>';
}
}
<?php
namespace IANRMedia\SpatialTools\Adapters;
use IANRMedia\SpatialTools\SpatialTools;
use IANRMedia\SpatialTools\Geometry\Geometry;
use IANRMedia\SpatialTools\Geometry\GeometryCollection;
use IANRMedia\SpatialTools\Geometry\Point;
use IANRMedia\SpatialTools\Geometry\MultiPoint;
use IANRMedia\SpatialTools\Geometry\Polygon;
use IANRMedia\SpatialTools\Geometry\MultiPolygon;
use IANRMedia\SpatialTools\Geometry\LineString;
use IANRMedia\SpatialTools\Geometry\MultiLineString;
use Exception;
use InvalidText;
use DOMDocument;
/*
* (c) Patrick Hayes
*
* This code is open-source and licenced under the Modified BSD License.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* PHP Geometry/WKB encoder/decoder
*
*/
class WKB extends GeoAdapter
{
private $dimension = 2;
private $z = FALSE;
private $m = FALSE;
/**
* Read WKB into geometry objects
*
* @param string $wkb
* Well-known-binary string
* @param bool $is_hex_string
* If this is a hexedecimal string that is in need of packing
*
* @return Geometry
*/
public function read($wkb, $is_hex_string = FALSE) {
if ($is_hex_string) {
$wkb = pack('H*',$wkb);
}
if (empty($wkb)) {
throw new Exception('Cannot read empty WKB geometry. Found ' . gettype($wkb));
}
$mem = fopen('php://memory', 'r+');
fwrite($mem, $wkb);
fseek($mem, 0);
$geometry = $this->getGeometry($mem);
fclose($mem);
return $geometry;
}
function getGeometry(&$mem) {
$base_info = unpack("corder/ctype/cz/cm/cs", fread($mem, 5));
if ($base_info['order'] !== 1) {
throw new Exception('Only NDR (little endian) SKB format is supported at the moment');
}
if ($base_info['z']) {
$this->dimension++;
$this->z = TRUE;
}
if ($base_info['m']) {
$this->dimension++;
$this->m = TRUE;
}
// If there is SRID information, ignore it - use EWKB Adapter to get SRID support
if ($base_info['s']) {
fread($mem, 4);
}
switch ($base_info['type']) {
case 1:
return $this->getPoint($mem);
case 2:
return $this->getLinstring($mem);
case 3:
return $this->getPolygon($mem);
case 4:
return $this->getMulti($mem,'point');
case 5:
return $this->getMulti($mem,'line');
case 6:
return $this->getMulti($mem,'polygon');
case 7:
return $this->getMulti($mem,'geometry');
}
}
function getPoint(&$mem) {
$point_coords = unpack("d*", fread($mem,$this->dimension*8));
if (!empty($point_coords)) {
return new Point($point_coords[1],$point_coords[2]);
}
else {
return new Point(); // EMPTY point
}
}
function getLinstring(&$mem) {
// Get the number of points expected in this string out of the first 4 bytes
$line_length = unpack('L',fread($mem,4));
// Return an empty linestring if there is no line-length
if (!$line_length[1]) return new LineString();
// Read the nubmer of points x2 (each point is two coords) into decimal-floats
$line_coords = unpack('d*', fread($mem,$line_length[1]*$this->dimension*8));
// We have our coords, build up the linestring
$components = array();
$i = 1;
$num_coords = count($line_coords);
while ($i <= $num_coords) {
$components[] = new Point($line_coords[$i],$line_coords[$i+1]);
$i += 2;
}
return new LineString($components);
}
function getPolygon(&$mem) {
// Get the number of linestring expected in this poly out of the first 4 bytes
$poly_length = unpack('L',fread($mem,4));
$components = array();
$i = 1;
while ($i <= $poly_length[1]) {
$components[] = $this->getLinstring($mem);
$i++;
}
return new Polygon($components);
}
function getMulti(&$mem, $type) {
// Get the number of items expected in this multi out of the first 4 bytes
$multi_length = unpack('L',fread($mem,4));
$components = array();
$i = 1;
while ($i <= $multi_length[1]) {
$components[] = $this->getGeometry($mem);
$i++;
}
switch ($type) {
case 'point':
return new MultiPoint($components);
case 'line':
return new MultiLineString($components);
case 'polygon':
return new MultiPolygon($components);
case 'geometry':
return new GeometryCollection($components);
}
}
/**
* Serialize geometries into WKB string.
*
* @param Geometry $geometry
*
* @return string The WKB string representation of the input geometries
*/
public function write(Geometry $geometry, $write_as_hex = FALSE) {
// We always write into NDR (little endian)
$wkb = pack('c',1);
switch ($geometry->getGeomType()) {
case 'Point';
$wkb .= pack('L',1);
$wkb .= $this->writePoint($geometry);
break;
case 'LineString';
$wkb .= pack('L',2);
$wkb .= $this->writeLineString($geometry);
break;
case 'Polygon';
$wkb .= pack('L',3);
$wkb .= $this->writePolygon($geometry);
break;
case 'MultiPoint';
$wkb .= pack('L',4);
$wkb .= $this->writeMulti($geometry);
break;
case 'MultiLineString';
$wkb .= pack('L',5);
$wkb .= $this->writeMulti($geometry);
break;
case 'MultiPolygon';
$wkb .= pack('L',6);
$wkb .= $this->writeMulti($geometry);
break;
case 'GeometryCollection';
$wkb .= pack('L',7);
$wkb .= $this->writeMulti($geometry);
break;
}
if ($write_as_hex) {
$unpacked = unpack('H*',$wkb);
return $unpacked[1];
}
else {
return $wkb;
}
}
function writePoint($point) {
// Set the coords
if (!$point->isEmpty()) {
$wkb = pack('dd',$point->x(), $point->y());
return $wkb;
} else {
return '';
}
}
function writeLineString($line) {
// Set the number of points in this line
$wkb = pack('L',$line->numPoints());
// Set the coords
foreach ($line->getComponents() as $point) {
$wkb .= pack('dd',$point->x(), $point->y());
}
return $wkb;
}
function writePolygon($poly) {
// Set the number of lines in this poly
$wkb = pack('L',$poly->numGeometries());
// Write the lines
foreach ($poly->getComponents() as $line) {
$wkb .= $this->writeLineString($line);
}
return $wkb;
}
function writeMulti($geometry) {
// Set the number of components
$wkb = pack('L',$geometry->numGeometries());
// Write the components
foreach ($geometry->getComponents() as $component) {
$wkb .= $this->write($component);
}
return $wkb;
}
}
<?php
namespace IANRMedia\SpatialTools\Adapters;
use IANRMedia\SpatialTools\SpatialTools;
use IANRMedia\SpatialTools\Geometry\Geometry;
use IANRMedia\SpatialTools\Geometry\GeometryCollection;
use IANRMedia\SpatialTools\Geometry\Point;
use IANRMedia\SpatialTools\Geometry\MultiPoint;
use IANRMedia\SpatialTools\Geometry\Polygon;
use IANRMedia\SpatialTools\Geometry\MultiPolygon;
use IANRMedia\SpatialTools\Geometry\LineString;
use IANRMedia\SpatialTools\Geometry\MultiLineString;
use GEOSWKTReader;
use GEOSWKTWriter;
/**
* WKT (Well Known Text) Adapter
*/
class WKT extends GeoAdapter
{
/**
* Read WKT string into geometry objects
*
* @param string $WKT A WKT string
*
* @return Geometry
*/
public function read($wkt) {
$wkt = trim($wkt);
// If it contains a ';', then it contains additional SRID data
if (strpos($wkt,';')) {
$parts = explode(';', $wkt);
$wkt = $parts[1];
$eparts = explode('=',$parts[0]);
$srid = $eparts[1];
}
else {
$srid = NULL;
}
// If geos is installed, then we take a shortcut and let it parse the WKT
if (SpatialTools::geosInstalled()) {
$reader = new GEOSWKTReader();
if ($srid) {
$geom = SpatialTools::geosToGeometry($reader->read($wkt));
$geom->setSRID($srid);
return $geom;
}
else {
return SpatialTools::geosToGeometry($reader->read($wkt));
}
}
$wkt = str_replace(', ', ',', $wkt);
// For each geometry type, check to see if we have a match at the
// beginning of the string. If we do, then parse using that type
foreach (SpatialTools::geometryList() as $geom_type) {
$wkt_geom = strtoupper($geom_type);
if (strtoupper(substr($wkt, 0, strlen($wkt_geom))) == $wkt_geom) {
$data_string = $this->getDataString($wkt);
$method = 'parse'.$geom_type;
if ($srid) {
$geom = $this->$method($data_string);
$geom->setSRID($srid);
return $geom;
}
else {
return $this->$method($data_string);
}
}
}
}
private function parsePoint($data_string) {
$data_string = $this->trimParens($data_string);
// If it's marked as empty, then return an empty point
if ($data_string == 'EMPTY') return new Point();
$parts = explode(' ',$data_string);
return new Point($parts[0], $parts[1]);
}
private function parseLineString($data_string) {
$data_string = $this->trimParens($data_string);
// If it's marked as empty, then return an empty line
if ($data_string == 'EMPTY') return new LineString();
$parts = explode(',',$data_string);
$points = array();
foreach ($parts as $part) {
$points[] = $this->parsePoint($part);
}
return new LineString($points);
}
private function parsePolygon($data_string) {
$data_string = $this->trimParens($data_string);
// If it's marked as empty, then return an empty polygon
if ($data_string == 'EMPTY') return new Polygon();
$parts = explode('),(',$data_string);
$lines = array();
foreach ($parts as $part) {
if (!$this->beginsWith($part,'(')) $part = '(' . $part;
if (!$this->endsWith($part,')')) $part = $part . ')';
$lines[] = $this->parseLineString($part);
}
return new Polygon($lines);
}
private function parseMultiPoint($data_string) {
$data_string = $this->trimParens($data_string);
// If it's marked as empty, then return an empty MutiPoint
if ($data_string == 'EMPTY') return new MultiPoint();
$parts = explode(',',$data_string);
$points = array();
foreach ($parts as $part) {
$points[] = $this->parsePoint($part);
}
return new MultiPoint($points);
}
private function parseMultiLineString($data_string) {
$data_string = $this->trimParens($data_string);
// If it's marked as empty, then return an empty multi-linestring
if ($data_string == 'EMPTY') return new MultiLineString();
$parts = explode('),(',$data_string);
$lines = array();
foreach ($parts as $part) {
// Repair the string if the explode broke it
if (!$this->beginsWith($part,'(')) $part = '(' . $part;
if (!$this->endsWith($part,')')) $part = $part . ')';
$lines[] = $this->parseLineString($part);
}
return new MultiLineString($lines);
}
private function parseMultiPolygon($data_string) {
$data_string = $this->trimParens($data_string);
// If it's marked as empty, then return an empty multi-polygon
if ($data_string == 'EMPTY') return new MultiPolygon();
$parts = explode(')),((',$data_string);
$polys = array();
foreach ($parts as $part) {
// Repair the string if the explode broke it
if (!$this->beginsWith($part,'((')) $part = '((' . $part;
if (!$this->endsWith($part,'))')) $part = $part . '))';
$polys[] = $this->parsePolygon($part);
}
return new MultiPolygon($polys);
}
private function parseGeometryCollection($data_string) {
$data_string = $this->trimParens($data_string);
// If it's marked as empty, then return an empty geom-collection
if ($data_string == 'EMPTY') return new GeometryCollection();
$geometries = array();
$matches = array();
$str = preg_replace('/,\s*([A-Za-z])/', '|$1', $data_string);
$components = explode('|', trim($str));
foreach ($components as $component) {
$geometries[] = $this->read($component);
}
return new GeometryCollection($geometries);
}
protected function getDataString($wkt) {
$first_paren = strpos($wkt, '(');
if ($first_paren !== FALSE) {
return substr($wkt, $first_paren);
} elseif (strstr($wkt,'EMPTY')) {
return 'EMPTY';
} else
return FALSE;
}
/**
* Trim the parenthesis and spaces
*/
protected function trimParens($str) {
$str = trim($str);
// We want to only strip off one set of parenthesis
if ($this->beginsWith($str, '(')) {
return substr($str,1,-1);
}
else return $str;
}
protected function beginsWith($str, $char) {
if (substr($str,0,strlen($char)) == $char) return TRUE;
else return FALSE;
}
protected function endsWith($str, $char) {
if (substr($str,(0 - strlen($char))) == $char) return TRUE;
else return FALSE;
}
/**
* Serialize geometries into a WKT string.
*
* @param Geometry $geometry
*
* @return string The WKT string representation of the input geometries
*/
public function write(Geometry $geometry) {
// If geos is installed, then we take a shortcut and let it write the WKT
if (SpatialTools::geosInstalled()) {
$writer = new GEOSWKTWriter();
$writer->setTrim(TRUE);
return $writer->write($geometry->geos());
}
if ($geometry->isEmpty()) {
return strtoupper($geometry->geometryType()).' EMPTY';
}
else if ($data = $this->extractData($geometry)) {
return strtoupper($geometry->geometryType()).' ('.$data.')';
}
}
/**
* Extract geometry to a WKT string
*
* @param Geometry $geometry A Geometry object
*
* @return string
*/
public function extractData($geometry) {
$parts = array();
switch ($geometry->geometryType()) {
case 'Point':
return $geometry->getX().' '.$geometry->getY();
case 'LineString':
foreach ($geometry->getComponents() as $component) {
$parts[] = $this->extractData($component);
}
return implode(', ', $parts);
case 'Polygon':
case 'MultiPoint':
case 'MultiLineString':
case 'MultiPolygon':
foreach ($geometry->getComponents() as $component) {
$parts[] = '('.$this->extractData($component).')';
}
return implode(', ', $parts);
case 'GeometryCollection':
foreach ($geometry->getComponents() as $component) {
$parts[] = strtoupper($component->geometryType()).' ('.$this->extractData($component).')';
}
return implode(', ', $parts);
}
}
}
<?php
namespace IANRMedia\SpatialTools\Eloquent;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use IANRMedia\SpatialTools\Geometry\Geometry;
class SpatialQueryBuilder extends EloquentBuilder {
public function update(array $values)
{
foreach ($values as $key => &$value) {
if ($value instanceof Geometry) {
$value = $this->asWKT($value);
}
}
return parent::update($values);
}
protected function asWKT(Geometry $geometry)
{
return $this->getQuery()->raw("ST_GeomFromText('".$geometry->out('wkt')."')");
}
}
<?php
namespace IANRMedia\SpatialTools\Eloquent;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use IANRMedia\SpatialTools\SpatialTools;
use IANRMedia\SpatialTools\Geometry\Geometry;
trait SpatialTrait {
public $geometries = [];
public function newEloquentBuilder($query){
return new SpatialQueryBuilder($query);
}
public function performInsert(EloquentBuilder $query, array $options = []){
foreach ($this->attributes as $key => $value) {
if ($value instanceof Geometry) {
$this->geometries[$key] = $value; //Preserve the geometry objects prior to the insert
$this->attributes[$key] = $this->getConnection()->raw(sprintf("ST_GeomFromText('%s')", $value->out('wkt')));
}
}
$insert = parent::performInsert($query, $options);
foreach ($this->geometries as $key => $value) {
$this->attributes[$key] = $value; //Retrieve the geometry objects so they can be used in the model
}
return $insert; //Return the result of the parent insert
}
public function setRawAttributes(array $attributes, $sync = false){
$spatial_fields = $this->getSpatialFields();
foreach ($attributes as $attribute => &$value) {
if (in_array($attribute, $spatial_fields) && is_string($value) && strlen($value) >= 15) {
$value = GeometryTools::fromWKB($value);
}
}
return parent::setRawAttributes($attributes, $sync);
}
public function getSpatialFields(){
if (property_exists($this, 'spatialFields')) {
return $this->spatialFields;
} else {
throw new Exception(__CLASS__.' has to define $spatialFields');
}
}
public function scopeDistance($query, $geometryColumn, $geometry, $distance) {
$query->whereRaw("st_distance(`{$geometryColumn}`, ST_GeomFromText('{$geometry->out('wkt')}')) <= {$distance}");
return $query;
}
public function scopeDistanceExcludingSelf($query, $geometryColumn, $geometry, $distance) {
$query = $this->scopeDistance($query, $geometryColumn, $geometry, $distance);
$query->whereRaw("st_distance(`{$geometryColumn}`, ST_GeomFromText('{$geometry->out('wkt')}')) != 0");
return $query;
}
public function scopeDistanceValue($query, $geometryColumn, $geometry) {
$columns = $query->getQuery()->columns;
if (!$columns) {
$query->select('*');
}
$query->selectRaw("st_distance(`{$geometryColumn}`, ST_GeomFromText('{$geometry->out('wkt')}')) as distance");
}
public function scopeDistanceSphere($query, $geometryColumn, $geometry, $distance) {
$query->whereRaw("st_distance_sphere(`{$geometryColumn}`, ST_GeomFromText('{$geometry->out('wkt')}')) <= {$distance}");
return $query;
}
public function scopeDistanceSphereExcludingSelf($query, $geometryColumn, $geometry, $distance) {
$query = $this->scopeDistanceSphere($query, $geometryColumn, $geometry, $distance);
$query->whereRaw("st_distance_sphere(`{$geometryColumn}`, ST_GeomFromText('{$geometry->out('wkt')}')) != 0");
return $query;
}
public function scopeDistanceSphereValue($query, $geometryColumn, $geometry) {
$columns = $query->getQuery()->columns;
if (!$columns) {
$query->select('*');
}
$query->selectRaw("st_distance_sphere(`{$geometryColumn}`, ST_GeomFromText('{$geometry->toWkt()}')) as distance");
}
public function scopeComparison($query, $geometryColumn, $geometry, $relationship) {
$query->whereRaw("st_{$relationship}(`{$geometryColumn}`, ST_GeomFromText('{$geometry->out('wkt')}'))");
return $query;
}
public function scopeWithin($query, $geometryColumn, $polygon) {
return $this->scopeComparison($query, $geometryColumn, $polygon, 'within');
}
public function scopeCrosses($query, $geometryColumn, $geometry) {
return $this->scopeComparison($query, $geometryColumn, $geometry, 'crosses');
}
public function scopeContains($query, $geometryColumn, $geometry) {
return $this->scopeComparison($query, $geometryColumn, $geometry, 'contains');
}
public function scopeDisjoint($query, $geometryColumn, $geometry) {
return $this->scopeComparison($query, $geometryColumn, $geometry, 'disjoint');
}
public function scopeEquals($query, $geometryColumn, $geometry) {
return $this->scopeComparison($query, $geometryColumn, $geometry, 'equals');
}
public function scopeIntersects($query, $geometryColumn, $geometry) {
return $this->scopeComparison($query, $geometryColumn, $geometry, 'intersects');
}
public function scopeOverlaps($query, $geometryColumn, $geometry) {
return $this->scopeComparison($query, $geometryColumn, $geometry, 'overlaps');
}
public function scopeDoesTouch($query, $geometryColumn, $geometry) {
return $this->scopeComparison($query, $geometryColumn, $geometry, 'touches');
}
}
<?php
namespace IANRMedia\SpatialTools\Geometry;
use IANRMedia\SpatialTools\SpatialTools;
use Exception;
/**
* Collection: Abstract class for compound geometries
*
* A geometry is a collection if it is made up of other
* component geometries. Therefore everything but a Point
* is a Collection. For example a LingString is a collection
* of Points. A Polygon is a collection of LineStrings etc.
*/
abstract class Collection extends Geometry
{
public $components = array();
/**
* Constructor: Checks and sets component geometries
*
* @param array $components array of geometries
*/
public function __construct($components = array()) {
if (!is_array($components)) {
throw new Exception("Component geometries must be passed as an array");
}
foreach ($components as $component) {
if ($component instanceof Geometry) {
$this->components[] = $component;
}
else {
throw new Exception("Cannot create a collection with non-geometries");
}
}
}
/**
* Returns Collection component geometries
*
* @return array
*/
public function getComponents() {
return $this->components;
}
/*
* Author : Adam Cherti
*
* inverts x and y coordinates
* Useful for old data still using lng lat
*
* @return void
*
* */
public function invertxy()
{
for($i=0;$i<count($this->components);$i++)
{
if( method_exists($this->components[$i], 'invertxy' ) )
$this->components[$i]->invertxy();
}
}
public function centroid() {
if ($this->isEmpty()) return NULL;
if ($this->geos()) {
$geos_centroid = $this->geos()->centroid();
if ($geos_centroid->typeName() == 'Point') {
return SpatialTools::geosToGeometry($this->geos()->centroid());
}
}
// As a rough estimate, we say that the centroid of a colletion is the centroid of it's envelope
// @@TODO: Make this the centroid of the convexHull
// Note: Outside of polygons, geometryCollections and the trivial case of points, there is no standard on what a "centroid" is
$centroid = $this->envelope()->centroid();
return $centroid;
}
public function getBBox() {
if ($this->isEmpty()) return NULL;
if ($this->geos()) {
$envelope = $this->geos()->envelope();
if ($envelope->typeName() == 'Point') {
return SpatialTools::geosToGeometry($envelope)->getBBOX();
}
$geos_ring = $envelope->exteriorRing();
return array(
'maxy' => $geos_ring->pointN(3)->getY(),
'miny' => $geos_ring->pointN(1)->getY(),
'maxx' => $geos_ring->pointN(1)->getX(),
'minx' => $geos_ring->pointN(3)->getX(),
);
}
// Go through each component and get the max and min x and y
$i = 0;
foreach ($this->components as $component) {
$component_bbox = $component->getBBox();
// On the first run through, set the bbox to the component bbox
if ($i == 0) {
$maxx = $component_bbox['maxx'];
$maxy = $component_bbox['maxy'];
$minx = $component_bbox['minx'];
$miny = $component_bbox['miny'];
}
// Do a check and replace on each boundary, slowly growing the bbox
$maxx = $component_bbox['maxx'] > $maxx ? $component_bbox['maxx'] : $maxx;
$maxy = $component_bbox['maxy'] > $maxy ? $component_bbox['maxy'] : $maxy;
$minx = $component_bbox['minx'] < $minx ? $component_bbox['minx'] : $minx;
$miny = $component_bbox['miny'] < $miny ? $component_bbox['miny'] : $miny;
$i++;
}
return array(
'maxy' => $maxy,
'miny' => $miny,
'maxx' => $maxx,
'minx' => $minx,
);
}
public function asArray() {
$array = array();
foreach ($this->components as $component) {
$array[] = $component->asArray();
}
return $array;
}
public function area() {
if ($this->geos()) {
return $this->geos()->area();
}
$area = 0;
foreach ($this->components as $component) {
$area += $component->area();
}
return $area;
}
// By default, the boundary of a collection is the boundary of it's components
public function boundary() {
if ($this->isEmpty()) return new LineString();
if ($this->geos()) {
return $this->geos()->boundary();
}
$components_boundaries = array();
foreach ($this->components as $component) {
$components_boundaries[] = $component->boundary();
}
return SpatialTools::geometryReduce($components_boundaries);
}
public function numGeometries() {
return count($this->components);
}
// Note that the standard is 1 based indexing
public function geometryN($n) {
$n = intval($n);
if (array_key_exists($n-1, $this->components)) {
return $this->components[$n-1];
}
else {
return NULL;
}
}
public function length() {
$length = 0;
foreach ($this->components as $delta => $component) {
$length += $component->length();
}
return $length;
}
public function greatCircleLength($radius = 6378137) {
$length = 0;
foreach ($this->components as $component) {
$length += $component->greatCircleLength($radius);
}
return $length;
}
public function haversineLength() {
$length = 0;
foreach ($this->components as $component) {
$length += $component->haversineLength();
}
return $length;
}
public function dimension() {
$dimension = 0;
foreach ($this->components as $component) {
if ($component->dimension() > $dimension) {
$dimension = $component->dimension();
}
}
return $dimension;
}
// A collection is empty if it has no components OR all it's components are empty
public function isEmpty() {
if (!count($this->components)) {
return TRUE;
}
else {
foreach ($this->components as $component) {
if (!$component->isEmpty()) return FALSE;
}
return TRUE;
}
}
public function numPoints() {
$num = 0;
foreach ($this->components as $component) {
$num += $component->numPoints();
}
return $num;
}
public function getPoints() {
$points = array();
foreach ($this->components as $component) {
$points = array_merge($points, $component->getPoints());
}
return $points;
}
public function equals($geometry) {
if ($this->geos()) {
return $this->geos()->equals($geometry->geos());
}
// To test for equality we check to make sure that there is a matching point
// in the other geometry for every point in this geometry.
// This is slightly more strict than the standard, which
// uses Within(A,B) = true and Within(B,A) = true
// @@TODO: Eventually we could fix this by using some sort of simplification
// method that strips redundant vertices (that are all in a row)
$this_points = $this->getPoints();
$other_points = $geometry->getPoints();
// First do a check to make sure they have the same number of vertices
if (count($this_points) != count($other_points)) {
return FALSE;
}
foreach ($this_points as $point) {
$found_match = FALSE;
foreach ($other_points as $key => $test_point) {
if ($point->equals($test_point)) {
$found_match = TRUE;
unset($other_points[$key]);
break;
}
}
if (!$found_match) {
return FALSE;
}
}
// All points match, return TRUE
return TRUE;
}
public function isSimple() {
if ($this->geos()) {
return $this->geos()->isSimple();
}
// A collection is simple if all it's components are simple
foreach ($this->components as $component) {
if (!$component->isSimple()) return FALSE;
}
return TRUE;
}
public function explode() {
$parts = array();
foreach ($this->components as $component) {
$parts = array_merge($parts, $component->explode());
}
return $parts;
}
// Not valid for this geometry type
// --------------------------------
public function x() { return NULL; }
public function y() { return NULL; }
public function startPoint() { return NULL; }
public function endPoint() { return NULL; }
public function isRing() { return NULL; }
public function isClosed() { return NULL; }
public function pointN($n) { return NULL; }
public function exteriorRing() { return NULL; }
public function numInteriorRings() { return NULL; }
public function interiorRingN($n) { return NULL; }
public function pointOnSurface() { return NULL; }
}
<?php
namespace IANRMedia\SpatialTools\Geometry;
use IANRMedia\SpatialTools\Geometry\Geometry;
use IANRMedia\SpatialTools\Geometry\GeometryCollection;
use IANRMedia\SpatialTools\Geometry\Point;
use IANRMedia\SpatialTools\Geometry\MultiPoint;
use IANRMedia\SpatialTools\Geometry\Polygon;
use IANRMedia\SpatialTools\Geometry\MultiPolygon;
use IANRMedia\SpatialTools\Geometry\LineString;
use IANRMedia\SpatialTools\Geometry\MultiLineString;
class Factory implements \GeoIO\Factory
{
public function createPoint($dimension, array $coordinates, $srid = null)
{
return new Point($coordinates['y'], $coordinates['x']);
}
public function createLineString($dimension, array $points, $srid = null)
{
return new LineString($points);
}
public function createLinearRing($dimension, array $points, $srid = null)
{
return new LineString($points);
}
public function createPolygon($dimension, array $lineStrings, $srid = null)
{
return new Polygon($lineStrings);
}
public function createMultiPoint($dimension, array $points, $srid = null)
{
return new MultiPoint($points);
}
public function createMultiLineString($dimension, array $lineStrings, $srid = null)
{
return new MultiLineString($lineStrings);
}
public function createMultiPolygon($dimension, array $polygons, $srid = null)
{
return new MultiPolygon($polygons);
}
public function createGeometryCollection($dimension, array $geometries, $srid = null)
{
return new GeometryCollection($geometries);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment