Przeglądaj źródła

Merge branch 'master' of bn.git:plabudda/se

Piotr Labudda 9 lat temu
rodzic
commit
ab041f17de
24 zmienionych plików z 3898 dodań i 0 usunięć
  1. 0 0
      SE/dev/sql/biale_plamy.sql
  2. 4 0
      SE/se-lib/Vendor/Geophp.php
  3. 370 0
      SE/se-lib/Vendor/geoPHP/LICENSE
  4. 301 0
      SE/se-lib/Vendor/geoPHP/geoPHP.inc
  5. 96 0
      SE/se-lib/Vendor/geoPHP/lib/adapters/EWKB.class.php
  6. 27 0
      SE/se-lib/Vendor/geoPHP/lib/adapters/EWKT.class.php
  7. 180 0
      SE/se-lib/Vendor/geoPHP/lib/adapters/GPX.class.php
  8. 31 0
      SE/se-lib/Vendor/geoPHP/lib/adapters/GeoAdapter.class.php
  9. 249 0
      SE/se-lib/Vendor/geoPHP/lib/adapters/GeoHash.class.php
  10. 155 0
      SE/se-lib/Vendor/geoPHP/lib/adapters/GeoJSON.class.php
  11. 244 0
      SE/se-lib/Vendor/geoPHP/lib/adapters/GeoRSS.class.php
  12. 158 0
      SE/se-lib/Vendor/geoPHP/lib/adapters/GoogleGeocode.class.php
  13. 272 0
      SE/se-lib/Vendor/geoPHP/lib/adapters/KML.class.php
  14. 250 0
      SE/se-lib/Vendor/geoPHP/lib/adapters/WKB.class.php
  15. 258 0
      SE/se-lib/Vendor/geoPHP/lib/adapters/WKT.class.php
  16. 294 0
      SE/se-lib/Vendor/geoPHP/lib/geometry/Collection.class.php
  17. 347 0
      SE/se-lib/Vendor/geoPHP/lib/geometry/Geometry.class.php
  18. 29 0
      SE/se-lib/Vendor/geoPHP/lib/geometry/GeometryCollection.class.php
  19. 190 0
      SE/se-lib/Vendor/geoPHP/lib/geometry/LineString.class.php
  20. 20 0
      SE/se-lib/Vendor/geoPHP/lib/geometry/MultiLineString.class.php
  21. 21 0
      SE/se-lib/Vendor/geoPHP/lib/geometry/MultiPoint.class.php
  22. 8 0
      SE/se-lib/Vendor/geoPHP/lib/geometry/MultiPolygon.class.php
  23. 179 0
      SE/se-lib/Vendor/geoPHP/lib/geometry/Point.class.php
  24. 215 0
      SE/se-lib/Vendor/geoPHP/lib/geometry/Polygon.class.php

+ 0 - 0
SE/sql-dev/biale_plamy.sql → SE/dev/sql/biale_plamy.sql


+ 4 - 0
SE/se-lib/Vendor/Geophp.php

@@ -0,0 +1,4 @@
+<?php
+require("geoPHP/geoPHP.inc");
+class Vendor_Geophp extends geoPHP {
+}

+ 370 - 0
SE/se-lib/Vendor/geoPHP/LICENSE

@@ -0,0 +1,370 @@
+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.

+ 301 - 0
SE/se-lib/Vendor/geoPHP/geoPHP.inc

@@ -0,0 +1,301 @@
+<?php
+/*
+ * (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.
+ */
+
+// Adapters
+include_once("lib/adapters/GeoAdapter.class.php"); // Abtract class
+include_once("lib/adapters/GeoJSON.class.php");
+include_once("lib/adapters/WKT.class.php");
+include_once("lib/adapters/EWKT.class.php");
+include_once("lib/adapters/WKB.class.php");
+include_once("lib/adapters/EWKB.class.php");
+include_once("lib/adapters/KML.class.php");
+include_once("lib/adapters/GPX.class.php");
+include_once("lib/adapters/GeoRSS.class.php");
+include_once("lib/adapters/GoogleGeocode.class.php");
+include_once("lib/adapters/GeoHash.class.php");
+
+// Geometries
+include_once("lib/geometry/Geometry.class.php"); // Abtract class
+include_once("lib/geometry/Point.class.php");
+include_once("lib/geometry/Collection.class.php"); // Abtract class
+include_once("lib/geometry/LineString.class.php");
+include_once("lib/geometry/MultiPoint.class.php");
+include_once("lib/geometry/Polygon.class.php");
+include_once("lib/geometry/MultiLineString.class.php");
+include_once("lib/geometry/MultiPolygon.class.php");
+include_once("lib/geometry/GeometryCollection.class.php");
+
+class geoPHP
+{
+
+  static function version() {
+    return '1.2';
+  }
+
+  // geoPHP::load($data, $type, $other_args);
+  // if $data is an array, all passed in values will be combined into a single geometry
+  static function load() {
+    $args = func_get_args();
+
+    $data = array_shift($args);
+    $type = array_shift($args);
+
+    $type_map = geoPHP::getAdapterMap();
+
+    // Auto-detect type if needed
+    if (!$type) {
+      // If the user is trying to load a Geometry from a Geometry... Just pass it back
+      if (is_object($data)) {
+        if ($data instanceOf Geometry) return $data;
+      }
+      
+      $detected = geoPHP::detectFormat($data);
+      if (!$detected) {
+        return FALSE;
+      }
+      
+      $format = explode(':', $detected);
+      $type = array_shift($format);
+      $args = $format;
+    }
+
+    $processor_type = $type_map[$type];
+
+    if (!$processor_type) {
+      throw new exception('geoPHP could not find an adapter of type '.htmlentities($type));
+      exit;
+    }
+
+    $processor = new $processor_type();
+
+    // Data is not an array, just pass it normally
+    if (!is_array($data)) {
+      $result = call_user_func_array(array($processor, "read"), array_merge(array($data), $args));
+    }
+    // Data is an array, combine all passed in items into a single geomtetry
+    else {
+      $geoms = array();
+      foreach ($data as $item) {
+        $geoms[] = call_user_func_array(array($processor, "read"), array_merge(array($item), $args));
+      }
+      $result = geoPHP::geometryReduce($geoms);
+    }
+
+    return $result;
+  }
+
+  static function getAdapterMap() {
+    return array (
+      'wkt' =>  'WKT',
+      'ewkt' => 'EWKT',
+      'wkb' =>  'WKB',
+      'ewkb' => 'EWKB',
+      'json' => 'GeoJSON',
+      'geojson' => 'GeoJSON',
+      'kml' =>  'KML',
+      'gpx' =>  'GPX',
+      'georss' => 'GeoRSS',
+      'google_geocode' => 'GoogleGeocode',
+      'geohash' => 'GeoHash',
+    );
+  }
+
+  static function geometryList() {
+    return array(
+      'point' => 'Point',
+      'linestring' => 'LineString',
+      'polygon' => 'Polygon',
+      'multipoint' => 'MultiPoint',
+      'multilinestring' => 'MultiLineString',
+      'multipolygon' => 'MultiPolygon',
+      'geometrycollection' => 'GeometryCollection',
+    );
+  }
+
+  static function geosInstalled($force = NULL) {
+    static $geos_installed = NULL;
+    if ($force !== NULL) $geos_installed = $force;
+    if ($geos_installed !== NULL) {
+      return $geos_installed;
+    }
+    $geos_installed = class_exists('GEOSGeometry');
+    return $geos_installed;
+  }
+
+  static function geosToGeometry($geos) {
+    if (!geoPHP::geosInstalled()) {
+      return NULL;
+    }
+    $wkb_writer = new GEOSWKBWriter();
+    $wkb = $wkb_writer->writeHEX($geos);
+    $geometry = geoPHP::load($wkb, 'wkb', TRUE);
+    if ($geometry) {
+      $geometry->setGeos($geos);
+      return $geometry;
+    }
+  }
+
+  // Reduce a geometry, or an array of geometries, into their 'lowest' available common geometry.
+  // For example a GeometryCollection of only points will become a MultiPoint
+  // A multi-point containing a single point will return a point.
+  // An array of geometries can be passed and they will be compiled into a single geometry
+  static function geometryReduce($geometry) {
+    // If it's an array of one, then just parse the one
+    if (is_array($geometry)) {
+      if (empty($geometry)) return FALSE;
+      if (count($geometry) == 1) return geoPHP::geometryReduce(array_shift($geometry));
+    }
+
+    // If the geometry cannot even theoretically be reduced more, then pass it back
+    if (gettype($geometry) == 'object') {
+      $passbacks = array('Point','LineString','Polygon');
+      if (in_array($geometry->geometryType(),$passbacks)) {
+        return $geometry;
+      }
+    }
+
+    // If it is a mutlti-geometry, check to see if it just has one member
+    // If it does, then pass the member, if not, then just pass back the geometry
+    if (gettype($geometry) == 'object') {
+      $simple_collections = array('MultiPoint','MultiLineString','MultiPolygon');
+      if (in_array(get_class($geometry),$passbacks)) {
+        $components = $geometry->getComponents();
+        if (count($components) == 1) {
+          return $components[0];
+        }
+        else {
+          return $geometry;
+        }
+      }
+    }
+
+    // So now we either have an array of geometries, a GeometryCollection, or an array of GeometryCollections
+    if (!is_array($geometry)) {
+      $geometry = array($geometry);
+    }
+
+    $geometries = array();
+    $geom_types = array();
+
+    $collections = array('MultiPoint','MultiLineString','MultiPolygon','GeometryCollection');
+
+    foreach ($geometry as $item) {
+      if ($item) {
+        if (in_array(get_class($item), $collections)) {
+          foreach ($item->getComponents() as $component) {
+            $geometries[] = $component;
+            $geom_types[] = $component->geometryType();
+          }
+        }
+        else {
+          $geometries[] = $item;
+          $geom_types[] = $item->geometryType();
+        }
+      }
+    }
+
+    $geom_types = array_unique($geom_types);
+    
+    if (empty($geom_types)) {
+      return FALSE;
+    }
+
+    if (count($geom_types) == 1) {
+      if (count($geometries) == 1) {
+        return $geometries[0];
+      }
+      else {
+        $class = 'Multi'.$geom_types[0];
+        return new $class($geometries);
+      }
+    }
+    else {
+      return new GeometryCollection($geometries);
+    }
+  }
+
+  // Detect a format given a value. This function is meant to be SPEEDY.
+  // It could make a mistake in XML detection if you are mixing or using namespaces in weird ways (ie, KML inside an RSS feed)
+  static function detectFormat(&$input) {
+    $mem = fopen('php://memory', 'r+');
+    fwrite($mem, $input, 11); // Write 11 bytes - we can detect the vast majority of formats in the first 11 bytes
+    fseek($mem, 0);
+
+    $bytes = unpack("c*", fread($mem, 11));
+
+    // If bytes is empty, then we were passed empty input
+    if (empty($bytes)) return FALSE;
+
+    // First char is a tab, space or carriage-return. trim it and try again
+    if ($bytes[1] == 9 || $bytes[1] == 10 || $bytes[1] == 32) {
+      $ltinput = ltrim($input);
+      return geoPHP::detectFormat($ltinput);
+    }
+
+    // Detect WKB or EWKB -- first byte is 1 (little endian indicator)
+    if ($bytes[1] == 1) {
+      // If SRID byte is TRUE (1), it's EWKB
+      if ($bytes[5]) return 'ewkb';
+      else return 'wkb';
+    }
+
+    // Detect HEX encoded WKB or EWKB (PostGIS format) -- first byte is 48, second byte is 49 (hex '01' => first-byte = 1)
+    if ($bytes[1] == 48 && $bytes[2] == 49) {
+      // The shortest possible WKB string (LINESTRING EMPTY) is 18 hex-chars (9 encoded bytes) long
+      // This differentiates it from a geohash, which is always shorter than 18 characters.
+      if (strlen($input) >= 18) {
+        //@@TODO: Differentiate between EWKB and WKB -- check hex-char 10 or 11 (SRID bool indicator at encoded byte 5)
+        return 'ewkb:1';
+      }
+    }
+
+    // Detect GeoJSON - first char starts with {
+    if ($bytes[1] == 123) {
+      return 'json';
+    }
+
+    // Detect EWKT - first char is S
+    if ($bytes[1] == 83) {
+      return 'ewkt';
+    }
+
+    // Detect WKT - first char starts with P (80), L (76), M (77), or G (71)
+    $wkt_chars = array(80, 76, 77, 71);
+    if (in_array($bytes[1], $wkt_chars)) {
+      return 'wkt';
+    }
+
+    // Detect XML -- first char is <
+    if ($bytes[1] == 60) {
+      // grab the first 256 characters
+      $string = substr($input, 0, 256);
+      if (strpos($string, '<kml') !== FALSE)        return 'kml';
+      if (strpos($string, '<coordinate') !== FALSE) return 'kml';
+      if (strpos($string, '<gpx') !== FALSE)        return 'gpx';
+      if (strpos($string, '<georss') !== FALSE)     return 'georss';
+      if (strpos($string, '<rss') !== FALSE)        return 'georss';
+      if (strpos($string, '<feed') !== FALSE)       return 'georss';
+    }
+
+    // We need an 8 byte string for geohash and unpacked WKB / WKT
+    fseek($mem, 0);
+    $string = trim(fread($mem, 8));
+
+    // Detect geohash - geohash ONLY contains lowercase chars and numerics
+    preg_match('/[a-z0-9]+/', $string, $matches);
+    if ($matches[0] == $string) {
+      return 'geohash';
+    }
+
+    // What do you get when you cross an elephant with a rhino?
+    // http://youtu.be/RCBn5J83Poc
+    return FALSE;
+  }
+
+}

+ 96 - 0
SE/se-lib/Vendor/geoPHP/lib/adapters/EWKB.class.php

@@ -0,0 +1,96 @@
+<?php
+/**
+ * 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;
+    }
+  }
+
+}

+ 27 - 0
SE/se-lib/Vendor/geoPHP/lib/adapters/EWKT.class.php

@@ -0,0 +1,27 @@
+<?php
+/**
+ * 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');
+    }
+  }
+}

+ 180 - 0
SE/se-lib/Vendor/geoPHP/lib/adapters/GPX.class.php

@@ -0,0 +1,180 @@
+<?php
+/*
+ * 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 geoPHP::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;
+  }
+
+}

+ 31 - 0
SE/se-lib/Vendor/geoPHP/lib/adapters/GeoAdapter.class.php

@@ -0,0 +1,31 @@
+<?php
+/*
+ * (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 Geomtry or GeometryCollection
+   * 
+   * @return Geometry|GeometryCollection
+   */
+  abstract public function read($input);
+  
+  /**
+   * Write out a Geomtry or GeometryCollection in the adapter's format
+   * 
+   * @return mixed
+   */
+  abstract public function write(Geometry $geometry);
+  
+}

+ 249 - 0
SE/se-lib/Vendor/geoPHP/lib/adapters/GeoHash.class.php

@@ -0,0 +1,249 @@
+<?php
+/**
+ * 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)];
+  }
+}

+ 155 - 0
SE/se-lib/Vendor/geoPHP/lib/adapters/GeoJSON.class.php

@@ -0,0 +1,155 @@
+<?php
+/**
+ * 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(),
+    );
+  }
+}
+
+

+ 244 - 0
SE/se-lib/Vendor/geoPHP/lib/adapters/GeoRSS.class.php

@@ -0,0 +1,244 @@
+<?php
+/*
+ * 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 geoPHP::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;
+  }
+
+}

+ 158 - 0
SE/se-lib/Vendor/geoPHP/lib/adapters/GoogleGeocode.class.php

@@ -0,0 +1,158 @@
+<?php
+/*
+ * (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;
+      }
+    }
+    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);
+  }
+}

+ 272 - 0
SE/se-lib/Vendor/geoPHP/lib/adapters/KML.class.php

@@ -0,0 +1,272 @@
+<?php
+/*
+ * 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 = geoPHP::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 geoPHP::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 = geoPHP::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>';
+  }
+
+}

+ 250 - 0
SE/se-lib/Vendor/geoPHP/lib/adapters/WKB.class.php

@@ -0,0 +1,250 @@
+<?php
+/*
+ * (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;
+  }
+
+}

+ 258 - 0
SE/se-lib/Vendor/geoPHP/lib/adapters/WKT.class.php

@@ -0,0 +1,258 @@
+<?php
+/**
+ * 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 (geoPHP::geosInstalled()) {
+      $reader = new GEOSWKTReader();
+      if ($srid) {
+        $geom = geoPHP::geosToGeometry($reader->read($wkt));
+        $geom->setSRID($srid);
+        return $geom;
+      }
+      else {
+        return geoPHP::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 (geoPHP::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 (geoPHP::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);
+    }
+  }
+}

+ 294 - 0
SE/se-lib/Vendor/geoPHP/lib/geometry/Collection.class.php

@@ -0,0 +1,294 @@
+<?php
+
+/**
+ * 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;
+  }
+
+  public function centroid() {
+    if ($this->isEmpty()) return NULL;
+
+    if ($this->geos()) {
+      $geos_centroid = $this->geos()->centroid();
+      if ($geos_centroid->typeName() == 'Point') {
+        return geoPHP::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 geoPHP::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 geoPHP::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; }
+}
+

+ 347 - 0
SE/se-lib/Vendor/geoPHP/lib/geometry/Geometry.class.php

@@ -0,0 +1,347 @@
+<?php
+
+/**
+ * Geometry abstract class
+ */
+abstract class Geometry
+{
+  private   $geos = NULL;
+  protected $srid = NULL;
+  protected $geom_type;
+
+  // Abtract: Standard
+  // -----------------
+  abstract public function area();
+  abstract public function boundary();
+  abstract public function centroid();
+  abstract public function length();
+  abstract public function y();
+  abstract public function x();
+  abstract public function numGeometries();
+  abstract public function geometryN($n);
+  abstract public function startPoint();
+  abstract public function endPoint();
+  abstract public function isRing();            // Mssing dependancy
+  abstract public function isClosed();          // Missing dependancy
+  abstract public function numPoints();
+  abstract public function pointN($n);
+  abstract public function exteriorRing();
+  abstract public function numInteriorRings();
+  abstract public function interiorRingN($n);
+  abstract public function dimension();
+  abstract public function equals($geom);
+  abstract public function isEmpty();
+  abstract public function isSimple();
+
+  // Abtract: Non-Standard
+  // ---------------------
+  abstract public function getBBox();
+  abstract public function asArray();
+  abstract public function getPoints();
+  abstract public function explode();
+  abstract public function greatCircleLength(); //meters
+  abstract public function haversineLength(); //degrees
+
+
+  // Public: Standard -- Common to all geometries
+  // --------------------------------------------
+  public function SRID() {
+    return $this->srid;
+  }
+
+  public function setSRID($srid) {
+    if ($this->geos()) {
+      $this->geos()->setSRID($srid);
+    }
+    $this->srid = $srid;
+  }
+
+  public function envelope() {
+    if ($this->isEmpty()) return new Polygon();
+
+    if ($this->geos()) {
+      return geoPHP::geosToGeometry($this->geos()->envelope());
+    }
+
+    $bbox = $this->getBBox();
+    $points = array (
+      new Point($bbox['maxx'],$bbox['miny']),
+      new Point($bbox['maxx'],$bbox['maxy']),
+      new Point($bbox['minx'],$bbox['maxy']),
+      new Point($bbox['minx'],$bbox['miny']),
+      new Point($bbox['maxx'],$bbox['miny']),
+    );
+
+    $outer_boundary = new LineString($points);
+    return new Polygon(array($outer_boundary));
+  }
+
+  public function geometryType() {
+    return $this->geom_type;
+  }
+
+  // Public: Non-Standard -- Common to all geometries
+  // ------------------------------------------------
+
+  // $this->out($format, $other_args);
+  public function out() {
+    $args = func_get_args();
+
+    $format = array_shift($args);
+    $type_map = geoPHP::getAdapterMap();
+    $processor_type = $type_map[$format];
+    $processor = new $processor_type();
+
+    array_unshift($args, $this);
+    $result = call_user_func_array(array($processor, 'write'), $args);
+
+    return $result;
+  }
+
+
+  // Public: Aliases
+  // ---------------
+  public function getCentroid() {
+    return $this->centroid();
+  }
+
+  public function getArea() {
+    return $this->area();
+  }
+
+  public function getX() {
+    return $this->x();
+  }
+
+  public function getY() {
+    return $this->y();
+  }
+
+  public function getGeos() {
+    return $this->geos();
+  }
+
+  public function getGeomType() {
+    return $this->geometryType();
+  }
+
+  public function getSRID() {
+    return $this->SRID();
+  }
+
+  public function asText() {
+    return $this->out('wkt');
+  }
+
+  public function asBinary() {
+    return $this->out('wkb');
+  }
+
+  // Public: GEOS Only Functions
+  // ---------------------------
+  public function geos() {
+    // If it's already been set, just return it
+    if ($this->geos && geoPHP::geosInstalled()) {
+      return $this->geos;
+    }
+    // It hasn't been set yet, generate it
+    if (geoPHP::geosInstalled()) {
+      $reader = new GEOSWKBReader();
+      $this->geos = $reader->readHEX($this->out('wkb',TRUE));
+    }
+    else {
+      $this->geos = FALSE;
+    }
+    return $this->geos;
+  }
+
+  public function setGeos($geos) {
+    $this->geos = $geos;
+  }
+
+  public function pointOnSurface() {
+    if ($this->geos()) {
+      return geoPHP::geosToGeometry($this->geos()->pointOnSurface());
+    }
+  }
+
+  public function equalsExact(Geometry $geometry) {
+    if ($this->geos()) {
+      return $this->geos()->equalsExact($geometry->geos());
+    }
+  }
+
+  public function relate(Geometry $geometry, $pattern = NULL) {
+    if ($this->geos()) {
+      if ($pattern) {
+        return $this->geos()->relate($geometry->geos(), $pattern);
+      }
+      else {
+        return $this->geos()->relate($geometry->geos());
+      }
+    }
+  }
+
+  public function checkValidity() {
+    if ($this->geos()) {
+      return $this->geos()->checkValidity();
+    }
+  }
+
+  public function buffer($distance) {
+    if ($this->geos()) {
+      return geoPHP::geosToGeometry($this->geos()->buffer($distance));
+    }
+  }
+
+  public function intersection(Geometry $geometry) {
+    if ($this->geos()) {
+      return geoPHP::geosToGeometry($this->geos()->intersection($geometry->geos()));
+    }
+  }
+
+  public function convexHull() {
+    if ($this->geos()) {
+      return geoPHP::geosToGeometry($this->geos()->convexHull());
+    }
+  }
+
+  public function difference(Geometry $geometry) {
+    if ($this->geos()) {
+      return geoPHP::geosToGeometry($this->geos()->difference($geometry->geos()));
+    }
+  }
+
+  public function symDifference(Geometry $geometry) {
+    if ($this->geos()) {
+      return geoPHP::geosToGeometry($this->geos()->symDifference($geometry->geos()));
+    }
+  }
+
+  // Can pass in a geometry or an array of geometries
+  public function union(Geometry $geometry) {
+    if ($this->geos()) {
+      if (is_array($geometry)) {
+        $geom = $this->geos();
+        foreach ($geometry as $item) {
+          $geom = $geom->union($item->geos());
+        }
+        return geoPHP::geosToGeometry($geom);
+      }
+      else {
+        return geoPHP::geosToGeometry($this->geos()->union($geometry->geos()));
+      }
+    }
+  }
+
+  public function simplify($tolerance, $preserveTopology = FALSE) {
+    if ($this->geos()) {
+      return geoPHP::geosToGeometry($this->geos()->simplify($tolerance, $preserveTopology));
+    }
+  }
+
+  public function disjoint(Geometry $geometry) {
+    if ($this->geos()) {
+      return $this->geos()->disjoint($geometry->geos());
+    }
+  }
+
+  public function touches(Geometry $geometry) {
+    if ($this->geos()) {
+      return $this->geos()->touches($geometry->geos());
+    }
+  }
+
+  public function intersects(Geometry $geometry) {
+    if ($this->geos()) {
+      return $this->geos()->intersects($geometry->geos());
+    }
+  }
+
+  public function crosses(Geometry $geometry) {
+    if ($this->geos()) {
+      return $this->geos()->crosses($geometry->geos());
+    }
+  }
+
+  public function within(Geometry $geometry) {
+    if ($this->geos()) {
+      return $this->geos()->within($geometry->geos());
+    }
+  }
+
+  public function contains(Geometry $geometry) {
+    if ($this->geos()) {
+      return $this->geos()->contains($geometry->geos());
+    }
+  }
+
+  public function overlaps(Geometry $geometry) {
+    if ($this->geos()) {
+      return $this->geos()->overlaps($geometry->geos());
+    }
+  }
+
+  public function covers(Geometry $geometry) {
+    if ($this->geos()) {
+      return $this->geos()->covers($geometry->geos());
+    }
+  }
+
+  public function coveredBy(Geometry $geometry) {
+    if ($this->geos()) {
+      return $this->geos()->coveredBy($geometry->geos());
+    }
+  }
+
+  public function distance(Geometry $geometry) {
+    if ($this->geos()) {
+      return $this->geos()->distance($geometry->geos());
+    }
+  }
+
+  public function hausdorffDistance(Geometry $geometry) {
+    if ($this->geos()) {
+      return $this->geos()->hausdorffDistance($geometry->geos());
+    }
+  }
+
+  public function project(Geometry $point, $normalized = NULL) {
+    if ($this->geos()) {
+      return $this->geos()->project($point->geos(), $normalized);
+    }
+  }
+
+  // Public - Placeholders
+  // ---------------------
+  public function hasZ() {
+    // geoPHP does not support Z values at the moment
+    return FALSE;
+  }
+
+  public function is3D() {
+    // geoPHP does not support 3D geometries at the moment
+    return FALSE;
+  }
+
+  public function isMeasured() {
+    // geoPHP does not yet support M values
+    return FALSE;
+  }
+
+  public function coordinateDimension() {
+    // geoPHP only supports 2-dimensional space
+    return 2;
+  }
+
+  public function z() {
+    // geoPHP only supports 2-dimensional space
+    return NULL;
+  }
+
+  public function m() {
+    // geoPHP only supports 2-dimensional space
+    return NULL;
+  }
+
+}

+ 29 - 0
SE/se-lib/Vendor/geoPHP/lib/geometry/GeometryCollection.class.php

@@ -0,0 +1,29 @@
+<?php
+/**
+ * GeometryCollection: A heterogenous collection of geometries  
+ */
+class GeometryCollection extends Collection 
+{
+  protected $geom_type = 'GeometryCollection';
+  
+  // We need to override asArray. Because geometryCollections are heterogeneous
+  // we need to specify which type of geometries they contain. We need to do this
+  // because, for example, there would be no way to tell the difference between a
+  // MultiPoint or a LineString, since they share the same structure (collection
+  // of points). So we need to call out the type explicitly. 
+  public function asArray() {
+    $array = array();
+    foreach ($this->components as $component) {
+      $array[] = array(
+        'type' => $component->geometryType(),
+        'components' => $component->asArray(),
+      );
+    }
+    return $array;
+  }
+  
+  // Not valid for this geomettry
+  public function boundary() { return NULL; }
+  public function isSimple() { return NULL; }
+}
+

+ 190 - 0
SE/se-lib/Vendor/geoPHP/lib/geometry/LineString.class.php

@@ -0,0 +1,190 @@
+<?php
+/**
+ * LineString. A collection of Points representing a line.
+ * A line can have more than one segment.
+ */
+class LineString extends Collection
+{
+  protected $geom_type = 'LineString';
+
+  /**
+   * Constructor
+   *
+   * @param array $points An array of at least two points with
+   * which to build the LineString
+   */
+  public function __construct($points = array()) {
+    if (count($points) == 1) {
+      throw new Exception("Cannot construct a LineString with a single point");
+    }
+
+    // Call the Collection constructor to build the LineString
+    parent::__construct($points);
+  }
+
+  // The boundary of a linestring is itself
+  public function boundary() {
+    return $this;
+  }
+
+  public function startPoint() {
+    return $this->pointN(1);
+  }
+
+  public function endPoint() {
+    $last_n = $this->numPoints();
+    return $this->pointN($last_n);
+  }
+
+  public function isClosed() {
+    return ($this->startPoint()->equals($this->endPoint()));
+  }
+
+  public function isRing() {
+    return ($this->isClosed() && $this->isSimple());
+  }
+
+  public function numPoints() {
+    return $this->numGeometries();
+  }
+
+  public function pointN($n) {
+    return $this->geometryN($n);
+  }
+
+  public function dimension() {
+    if ($this->isEmpty()) return 0;
+    return 1;
+  }
+
+  public function area() {
+    return 0;
+  }
+
+  public function length() {
+    if ($this->geos()) {
+      return $this->geos()->length();
+    }
+    $length = 0;
+    foreach ($this->getPoints() as $delta => $point) {
+      $previous_point = $this->geometryN($delta);
+      if ($previous_point) {
+        $length += sqrt(pow(($previous_point->getX() - $point->getX()), 2) + pow(($previous_point->getY()- $point->getY()), 2));
+      }
+    }
+    return $length;
+  }
+
+  public function greatCircleLength($radius = 6378137) {
+    $length = 0;
+    $points = $this->getPoints();
+    for($i=0; $i<$this->numPoints()-1; $i++) {
+      $point = $points[$i];
+      $next_point = $points[$i+1];
+      if (!is_object($next_point)) {continue;}
+      // Great circle method
+      $lat1 = deg2rad($point->getY());
+      $lat2 = deg2rad($next_point->getY());
+      $lon1 = deg2rad($point->getX());
+      $lon2 = deg2rad($next_point->getX());
+      $dlon = $lon2 - $lon1;
+      $length +=
+        $radius *
+          atan2(
+            sqrt(
+              pow(cos($lat2) * sin($dlon), 2) +
+                pow(cos($lat1) * sin($lat2) - sin($lat1) * cos($lat2) * cos($dlon), 2)
+            )
+            ,
+            sin($lat1) * sin($lat2) +
+              cos($lat1) * cos($lat2) * cos($dlon)
+          );
+    }
+    // Returns length in meters.
+    return $length;
+  }
+
+  public function haversineLength() {
+    $degrees = 0;
+    $points = $this->getPoints();
+    for($i=0; $i<$this->numPoints()-1; $i++) {
+      $point = $points[$i];
+      $next_point = $points[$i+1];
+      if (!is_object($next_point)) {continue;}
+      $degree = rad2deg(
+        acos(
+          sin(deg2rad($point->getY())) * sin(deg2rad($next_point->getY())) +
+            cos(deg2rad($point->getY())) * cos(deg2rad($next_point->getY())) *
+              cos(deg2rad(abs($point->getX() - $next_point->getX())))
+        )
+      );
+      $degrees += $degree;
+    }
+    // Returns degrees
+    return $degrees;
+  }
+
+  public function explode() {
+    $parts = array();
+    $points = $this->getPoints();
+
+    foreach ($points as $i => $point) {
+      if (isset($points[$i+1])) {
+        $parts[] = new LineString(array($point, $points[$i+1]));
+      }
+    }
+    return $parts;
+  }
+
+  public function isSimple() {
+    if ($this->geos()) {
+      return $this->geos()->isSimple();
+    }
+
+    $segments = $this->explode();
+
+    foreach ($segments as $i => $segment) {
+      foreach ($segments as $j => $check_segment) {
+        if ($i != $j) {
+          if ($segment->lineSegmentIntersect($check_segment)) {
+            return FALSE;
+          }
+        }
+      }
+    }
+    return TRUE;
+  }
+
+  // Utility function to check if any line sigments intersect
+  // Derived from http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
+  public function lineSegmentIntersect($segment) {
+    $p0_x = $this->startPoint()->x();
+    $p0_y = $this->startPoint()->y();
+    $p1_x = $this->endPoint()->x();
+    $p1_y = $this->endPoint()->y();
+    $p2_x = $segment->startPoint()->x();
+    $p2_y = $segment->startPoint()->y();
+    $p3_x = $segment->endPoint()->x();
+    $p3_y = $segment->endPoint()->y();
+
+    $s1_x = $p1_x - $p0_x;     $s1_y = $p1_y - $p0_y;
+    $s2_x = $p3_x - $p2_x;     $s2_y = $p3_y - $p2_y;
+
+    $fps = (-$s2_x * $s1_y) + ($s1_x * $s2_y);
+    $fpt = (-$s2_x * $s1_y) + ($s1_x * $s2_y);
+
+    if ($fps == 0 || $fpt == 0) {
+      return FALSE;
+    }
+
+    $s = (-$s1_y * ($p0_x - $p2_x) + $s1_x * ($p0_y - $p2_y)) / $fps;
+    $t = ( $s2_x * ($p0_y - $p2_y) - $s2_y * ($p0_x - $p2_x)) / $fpt;
+
+    if ($s > 0 && $s < 1 && $t > 0 && $t < 1) {
+      // Collision detected
+      return TRUE;
+    }
+    return FALSE;
+  }
+}
+

+ 20 - 0
SE/se-lib/Vendor/geoPHP/lib/geometry/MultiLineString.class.php

@@ -0,0 +1,20 @@
+<?php
+/**
+ * MultiLineString: A collection of LineStrings
+ */
+class MultiLineString extends Collection
+{
+  protected $geom_type = 'MultiLineString';
+
+  // MultiLineString is closed if all it's components are closed
+  public function isClosed() {
+    foreach ($this->components as $line) {
+      if (!$line->isClosed()) {
+        return FALSE;
+      }
+    }
+    return TRUE;
+  }
+
+}
+

+ 21 - 0
SE/se-lib/Vendor/geoPHP/lib/geometry/MultiPoint.class.php

@@ -0,0 +1,21 @@
+<?php
+/**
+ * MultiPoint: A collection Points  
+ */
+class MultiPoint extends Collection
+{
+  protected $geom_type = 'MultiPoint';
+  
+  public function numPoints() {
+    return $this->numGeometries();
+  }
+  
+  public function isSimple() {
+    return TRUE;
+  }
+  
+  // Not valid for this geometry type
+  // --------------------------------
+  public function explode() { return NULL; }
+}
+

+ 8 - 0
SE/se-lib/Vendor/geoPHP/lib/geometry/MultiPolygon.class.php

@@ -0,0 +1,8 @@
+<?php
+/**
+ * MultiPolygon: A collection of Polygons
+ */
+class MultiPolygon extends Collection 
+{
+  protected $geom_type = 'MultiPolygon';
+}

+ 179 - 0
SE/se-lib/Vendor/geoPHP/lib/geometry/Point.class.php

@@ -0,0 +1,179 @@
+<?php
+
+/**
+ * Point: The most basic geometry type. All other geometries
+ * are built out of Points.
+ */
+class Point extends Geometry
+{
+  public $coords = array(2);
+  protected $geom_type = 'Point';
+  protected $dimension = 2;
+
+  /**
+   * Constructor
+   *
+   * @param numeric $x The x coordinate (or longitude)
+   * @param numeric $y The y coordinate (or latitude)
+   * @param numeric $z The z coordinate (or altitude) - optional
+   */
+  public function __construct($x = NULL, $y = NULL, $z = NULL) {
+
+    // Check if it's an empty point
+    if ($x === NULL && $y === NULL) {
+      $this->coords = array(NULL, NULL);
+      $this->dimension = 0;
+      return;
+    }
+
+    // Basic validation on x and y
+    if (!is_numeric($x) || !is_numeric($y)) {
+      throw new Exception("Cannot construct Point. x and y should be numeric");
+    }
+
+    // Check to see if this is a 3D point
+    if ($z !== NULL) {
+      if (!is_numeric($z)) {
+       throw new Exception("Cannot construct Point. z should be numeric");
+      }
+      $this->dimension = 3;
+    }
+
+    // Convert to floatval in case they are passed in as a string or integer etc.
+    $x = floatval($x);
+    $y = floatval($y);
+    $z = floatval($z);
+
+    // Add poitional elements
+    if ($this->dimension == 2) {
+      $this->coords = array($x, $y);
+    }
+    if ($this->dimension == 3) {
+      $this->coords = array($x, $y, $z);
+    }
+  }
+
+  /**
+   * Get X (longitude) coordinate
+   *
+   * @return float The X coordinate
+   */
+  public function x() {
+    return $this->coords[0];
+  }
+
+  /**
+   * Returns Y (latitude) coordinate
+   *
+   * @return float The Y coordinate
+   */
+  public function y() {
+    return $this->coords[1];
+  }
+
+  /**
+   * Returns Z (altitude) coordinate
+   *
+   * @return float The Z coordinate or NULL is not a 3D point
+   */
+  public function z() {
+    if ($this->dimension == 3) {
+      return $this->coords[2];
+    }
+    else return NULL;
+  }
+
+  // A point's centroid is itself
+  public function centroid() {
+    return $this;
+  }
+
+  public function getBBox() {
+    return array(
+      'maxy' => $this->getY(),
+      'miny' => $this->getY(),
+      'maxx' => $this->getX(),
+      'minx' => $this->getX(),
+    );
+  }
+
+  public function asArray($assoc = FALSE) {
+    return $this->coords;
+  }
+
+  public function area() {
+    return 0;
+  }
+
+  public function length() {
+    return 0;
+  }
+
+  public function greatCircleLength() {
+    return 0;
+  }
+
+  public function haversineLength() {
+    return 0;
+  }
+
+  // The boundary of a point is itself
+  public function boundary() {
+    return $this;
+  }
+
+  public function dimension() {
+    return 0;
+  }
+
+  public function isEmpty() {
+    if ($this->dimension == 0) {
+      return TRUE;
+    }
+    else {
+      return FALSE;
+    }
+  }
+
+  public function numPoints() {
+    return 1;
+  }
+
+  public function getPoints() {
+    return array($this);
+  }
+
+  public function equals($geometry) {
+    if (get_class($geometry) != 'Point') {
+      return FALSE;
+    }
+    if (!$this->isEmpty() && !$geometry->isEmpty()) {
+      return ($this->x() == $geometry->x() && $this->y() == $geometry->y());
+    }
+    else if ($this->isEmpty() && $geometry->isEmpty()) {
+      return TRUE;
+    }
+    else {
+      return FALSE;
+    }
+  }
+
+  public function isSimple() {
+    return TRUE;
+  }
+
+  // Not valid for this geometry type
+  public function numGeometries()    { return NULL; }
+  public function geometryN($n)      { 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; }
+  public function explode()          { return NULL; }
+}
+

+ 215 - 0
SE/se-lib/Vendor/geoPHP/lib/geometry/Polygon.class.php

@@ -0,0 +1,215 @@
+<?php
+
+/**
+ * Polygon: A polygon is a plane figure that is bounded by a closed path, 
+ * composed of a finite sequence of straight line segments
+ */
+class Polygon extends Collection
+{
+  protected $geom_type = 'Polygon';
+
+  // The boundary of a polygin is it's outer ring
+  public function boundary() {
+    return $this->exteriorRing();
+  }
+
+  public function area($exterior_only = FALSE, $signed = FALSE) {
+    if ($this->isEmpty()) return 0;
+    
+    if ($this->geos() && $exterior_only == FALSE) {
+      return $this->geos()->area();
+    }
+    
+    $exterior_ring = $this->components[0];
+    $pts = $exterior_ring->getComponents();
+    
+    $c = count($pts);
+    if((int)$c == '0') return NULL;
+    $a = '0';
+    foreach($pts as $k => $p){
+      $j = ($k + 1) % $c;
+      $a = $a + ($p->getX() * $pts[$j]->getY()) - ($p->getY() * $pts[$j]->getX());
+    }
+    
+    if ($signed) $area = ($a / 2);
+    else $area = abs(($a / 2));
+    
+    if ($exterior_only == TRUE) {
+      return $area;
+    }
+    foreach ($this->components as $delta => $component) {
+      if ($delta != 0) {
+        $inner_poly = new Polygon(array($component));
+        $area -= $inner_poly->area();
+      }
+    }
+    return $area;
+  }
+  
+  public function centroid() {
+    if ($this->isEmpty()) return NULL;
+    
+    if ($this->geos()) {
+      return geoPHP::geosToGeometry($this->geos()->centroid());
+    }
+    
+    $exterior_ring = $this->components[0];
+    $pts = $exterior_ring->getComponents();
+    
+    $c = count($pts);
+    if((int)$c == '0') return NULL;
+    $cn = array('x' => '0', 'y' => '0');
+    $a = $this->area(TRUE, TRUE);
+    
+    // If this is a polygon with no area. Just return the first point.
+    if ($a == 0) {
+      return $this->exteriorRing()->pointN(1);
+    }
+    
+    foreach($pts as $k => $p){
+      $j = ($k + 1) % $c;
+      $P = ($p->getX() * $pts[$j]->getY()) - ($p->getY() * $pts[$j]->getX());
+      $cn['x'] = $cn['x'] + ($p->getX() + $pts[$j]->getX()) * $P;
+      $cn['y'] = $cn['y'] + ($p->getY() + $pts[$j]->getY()) * $P;
+    }
+    
+    $cn['x'] = $cn['x'] / ( 6 * $a);
+    $cn['y'] = $cn['y'] / ( 6 * $a);
+    
+    $centroid = new Point($cn['x'], $cn['y']);
+    return $centroid;
+  }
+
+	/**
+	 * Find the outermost point from the centroid
+	 *
+	 * @returns Point The outermost point
+	 */
+  public function outermostPoint() {
+		$centroid = $this->getCentroid();
+
+		$max = array('length' => 0, 'point' => null);
+
+		foreach($this->getPoints() as $point) {
+			$lineString = new LineString(array($centroid, $point));
+
+			if($lineString->length() > $max['length']) {
+				$max['length'] = $lineString->length();
+				$max['point'] = $point;
+			}
+		}
+
+		return $max['point'];
+  }
+
+  public function exteriorRing() {
+    if ($this->isEmpty()) return new LineString();
+    return $this->components[0];
+  }
+  
+  public function numInteriorRings() {
+    if ($this->isEmpty()) return 0;
+    return $this->numGeometries()-1;
+  }
+  
+  public function interiorRingN($n) {
+    return $this->geometryN($n+1);
+  }
+  
+  public function dimension() {
+    if ($this->isEmpty()) return 0;
+    return 2;
+  }
+
+  public function isSimple() {
+    if ($this->geos()) {
+      return $this->geos()->isSimple();
+    }
+    
+    $segments = $this->explode();
+    
+    foreach ($segments as $i => $segment) {
+      foreach ($segments as $j => $check_segment) {
+        if ($i != $j) {
+          if ($segment->lineSegmentIntersect($check_segment)) {
+            return FALSE;
+          }
+        }
+      }
+    }
+    return TRUE;
+  }
+
+  /**
+   * For a given point, determine whether it's bounded by the given polygon.
+   * Adapted from http://www.assemblysys.com/dataServices/php_pointinpolygon.php
+   * @see http://en.wikipedia.org/wiki/Point%5Fin%5Fpolygon
+   *
+   * @param Point $point 
+   * @param boolean $pointOnBoundary - whether a boundary should be considered "in" or not
+   * @param boolean $pointOnVertex - whether a vertex should be considered "in" or not
+   * @return boolean
+   */
+  public function pointInPolygon($point, $pointOnBoundary = true, $pointOnVertex = true) {
+    $vertices = $this->getPoints();
+
+    // Check if the point sits exactly on a vertex
+    if ($this->pointOnVertex($point, $vertices)) {
+      return $pointOnVertex ? TRUE : FALSE;
+    }
+  
+    // Check if the point is inside the polygon or on the boundary
+    $intersections = 0; 
+    $vertices_count = count($vertices);
+
+    for ($i=1; $i < $vertices_count; $i++) {
+      $vertex1 = $vertices[$i-1]; 
+      $vertex2 = $vertices[$i];
+      if ($vertex1->y() == $vertex2->y() 
+      && $vertex1->y() == $point->y() 
+      && $point->x() > min($vertex1->x(), $vertex2->x()) 
+      && $point->x() < max($vertex1->x(), $vertex2->x())) {
+        // Check if point is on an horizontal polygon boundary
+        return $pointOnBoundary ? TRUE : FALSE;
+      }
+      if ($point->y() > min($vertex1->y(), $vertex2->y())
+      && $point->y() <= max($vertex1->y(), $vertex2->y())
+      && $point->x() <= max($vertex1->x(), $vertex2->x())
+      && $vertex1->y() != $vertex2->y()) {
+        $xinters = 
+          ($point->y() - $vertex1->y()) * ($vertex2->x() - $vertex1->x())
+          / ($vertex2->y() - $vertex1->y()) 
+          + $vertex1->x();
+        if ($xinters == $point->x()) {
+          // Check if point is on the polygon boundary (other than horizontal)
+          return $pointOnBoundary ? TRUE : FALSE;
+        }
+        if ($vertex1->x() == $vertex2->x() || $point->x() <= $xinters) {
+          $intersections++;
+        }
+      } 
+    } 
+    // If the number of edges we passed through is even, then it's in the polygon.
+    if ($intersections % 2 != 0) {
+      return TRUE;
+    }
+    else {
+      return FALSE;
+    }
+  }
+  
+  public function pointOnVertex($point) {
+    foreach($this->getPoints() as $vertex) {
+      if ($point->equals($vertex)) {
+        return true;
+      }
+    }
+  }
+
+
+  // Not valid for this geometry type
+  // --------------------------------
+  public function length() { return NULL; }
+  
+}
+