Commit fe778f797fde7be21b291dcd923a91aa90c028bc

Authored by Edmar Moretti
1 parent 7e0f3272
Exists in master

Adição da biblioteca gisconverter e conversor WFS para Geojson sem uso do OGR

guia_de_migracao.txt
... ... @@ -3,6 +3,8 @@ GUIA DE UPDATES e UPGRADES
3 3 -------------------------------------------------------------------------------------------------
4 4 Para a versão 6.0
5 5  
  6 +- Inclusão da variável $ogrOutput em ms_configura.php, que indica se o OGR esta corretamente instalado, permitindo o seu uso nos servicos OGC de exportacao de dados
  7 +
6 8 - para permitir o retorno da requisição WMS getfeatureinfo em JSON, foi incluído no mapfile base utilizado para
7 9 gerar os serviços OGC a linha "wms_feature_info_mime_type" "application/json" em WEB->METADATA
8 10 Caso você utilize um mapfile específico da sua instalação, veja o arquivo original existente em i3geo/aplicmap
... ...
ms_configura.php
... ... @@ -74,6 +74,12 @@ $statusFerramentas = array(
74 74 "melhorcaminho"=>true
75 75 );
76 76 /*
  77 +Variable: $ogrOutput
  78 +
  79 +Indica se o OGR esta corretamente instalado, permitindo o seu uso nos servicos OGC de exportacao de dados
  80 +*/
  81 +$ogrOutput = true;
  82 +/*
77 83 Variable: saikuUrl
78 84  
79 85 URL para acessar o aplicativo SAIKU. Se nao estiver instalado, deixe em branco
... ...
ogc.php
... ... @@ -439,10 +439,8 @@ else{
439 439 if(!empty($parametro)){
440 440 $l->setfilter($parametro);
441 441 $cache = false;
442   - }
443   -
  442 + }
444 443 ms_newLayerObj($oMap, $l);
445   - //$req->setParameter("LAYERS", "mundo");
446 444 }
447 445 }
448 446 }
... ... @@ -818,9 +816,6 @@ if(strtolower($req->getValueByName("REQUEST")) == "getfeatureinfo" && $_GET["inf
818 816 getfeatureinfoJson();
819 817 exit;
820 818 }
821   -$oMap->owsdispatch($req);
822   -
823   -$contenttype = ms_iostripstdoutbuffercontenttype();
824 819  
825 820 if(strtolower($request) == "getcapabilities"){
826 821 header('Content-Disposition: attachment; filename=getcapabilities.xml');
... ... @@ -829,12 +824,19 @@ if(strtolower($request) == "getcapabilities"){
829 824 if(!isset($OUTPUTFORMAT)){
830 825 header("Content-type: $contenttype");
831 826 }
  827 +//$ogrOutput vem de ms_configura.php
832 828  
833 829 //precisa limpar o cabecalho
834 830 if(strtolower($OUTPUTFORMAT) == "geojson" || strtolower($OUTPUTFORMAT) == "json"){
  831 + $arq = $dir_tmp."/".$tema.".json";
  832 + if(isset($ogrOutput) && $ogrOutput == false){
  833 + exportaGeojson();
  834 + exit;
  835 + }
  836 + $oMap->owsdispatch($req);
  837 + $contenttype = ms_iostripstdoutbuffercontenttype();
835 838 ms_iostripstdoutbuffercontentheaders();
836 839 //grava em disco
837   - $arq = $dir_tmp."/".$tema.".json";
838 840 $contents = ms_iogetstdoutbufferstring();
839 841 file_put_contents($arq,$contents);
840 842 //envia para download
... ... @@ -844,6 +846,8 @@ if(strtolower($OUTPUTFORMAT) == "geojson" || strtolower($OUTPUTFORMAT) == "json"
844 846 exit;
845 847 }
846 848 if(strtolower($OUTPUTFORMAT) == "shape-zip"){
  849 + $oMap->owsdispatch($req);
  850 + $contenttype = ms_iostripstdoutbuffercontenttype();
847 851 //grava em disco
848 852 $arq = $dir_tmp."/".$tema."_shapefile.zip";
849 853 $contents = ms_iogetstdoutbufferstring();
... ... @@ -855,18 +859,28 @@ if(strtolower($OUTPUTFORMAT) == "shape-zip"){
855 859 exit;
856 860 }
857 861 if(strtolower($OUTPUTFORMAT) == "csv"){
  862 + $arq = $dir_tmp."/".$tema.$ows_geomtype.".csv";
  863 + $fileName = $tema.$ows_geomtype.'.csv';
  864 + if(isset($ogrOutput) && $ogrOutput == false){
  865 + exportaCsv();
  866 + exit;
  867 + }
  868 + //
  869 + $oMap->owsdispatch($req);
  870 + $contenttype = ms_iostripstdoutbuffercontenttype();
858 871 ms_iostripstdoutbuffercontentheaders();
859 872 //grava em disco
860   - $arq = $dir_tmp."/".$tema.$ows_geomtype.".csv";
861 873 $contents = ms_iogetstdoutbufferstring();
862 874 file_put_contents($arq,$contents);
863 875 //envia para download
864   - header('Content-Disposition: attachment; filename='.$tema.$ows_geomtype.'.csv');
  876 + header('Content-Disposition: attachment; filename='.$fileName);
865 877 header("Content-type: text/csv");
866 878 ms_iogetStdoutBufferBytes();
867 879 ms_ioresethandlers();
868 880 exit;
869   -}
  881 +}
  882 +$oMap->owsdispatch($req);
  883 +$contenttype = ms_iostripstdoutbuffercontenttype();
870 884 $buffer = ms_iogetStdoutBufferBytes();
871 885  
872 886 ms_ioresethandlers();
... ... @@ -1209,6 +1223,98 @@ function restauraMapaSalvo(){
1209 1223 $l = $m->getlayer(0);
1210 1224 $_GET["LAYERS"] = $l->name;
1211 1225 }
  1226 +function exportaCsv(){
  1227 + global $oMap, $arq, $fileName, $versao, $ows_geomtype;
  1228 + $layer = $oMap->getlayer(0);
  1229 + $items = pegaItens($layer,$oMap);
  1230 + $layer->querybyrect($oMap->extent);
  1231 + $layer->open();
  1232 + $res_count = $layer->getNumresults();
  1233 + $linhas = array();
  1234 + if($ows_geomtype == "none"){
  1235 + $linhas[] = implode(",",$items);
  1236 + }
  1237 + else{
  1238 + $linhas[] = implode(",",$items).",wkt";
  1239 + }
  1240 + for ($i = 0; $i < $res_count; $i++){
  1241 + if($versao == 6){
  1242 + $shape = $layer->getShape($layer->getResult($i));
  1243 + }
  1244 + else{
  1245 + $shape = $layer->getFeature($layer->getResult($i)->shapeindex);
  1246 + }
  1247 + $reg = array();
  1248 + foreach ($items as $item){
  1249 + $v = trim($shape->values[$item]);
  1250 + if(is_string($v)){
  1251 + $v = '"'.converteenc($v).'"';
  1252 + }
  1253 + $reg[] = $v;
  1254 + }
  1255 + if($ows_geomtype != "none"){
  1256 + $reg[] = '"'.$shape->towkt().'"';
  1257 + }
  1258 + $linhas[] = implode(",",$reg);
  1259 +
  1260 + }
  1261 + $contents = implode("\n",$linhas);
  1262 + file_put_contents($arq,$contents);
  1263 + //envia para download
  1264 + ob_clean();
  1265 + header('Content-Disposition: attachment; filename='.$fileName);
  1266 + header("Content-type: text/csv");
  1267 + echo $contents;
  1268 + exit;
  1269 +}
  1270 +function exportaGeojson(){
  1271 + global $oMap, $arq, $fileName, $versao, $ows_geomtype;
  1272 + include("pacotes/gisconverter/gisconverter.php");
  1273 + $decoder = new gisconverter\WKT();
  1274 + $layer = $oMap->getlayer(0);
  1275 + $items = pegaItens($layer,$oMap);
  1276 + $layer->querybyrect($oMap->extent);
  1277 + $layer->open();
  1278 + $res_count = $layer->getNumresults();
  1279 + $linhas = array();
  1280 +
  1281 + $features = array();
  1282 + for ($i = 0; $i < $res_count; $i++){
  1283 + if($versao == 6){
  1284 + $shape = $layer->getShape($layer->getResult($i));
  1285 + }
  1286 + else{
  1287 + $shape = $layer->getFeature($layer->getResult($i)->shapeindex);
  1288 + }
  1289 + $propriedades = array();
  1290 + foreach ($items as $item){
  1291 + $v = trim($shape->values[$item]);
  1292 + if(is_string($v)){
  1293 + $v = '"'.converteenc($v).'"';
  1294 + }
  1295 + $propriedades[] = array($item=>$v);
  1296 + }
  1297 + $wkt = $shape->towkt();
  1298 +
  1299 + $features[] = array(
  1300 + "type"=>"Feature",
  1301 + "properties"=>$propriedades,
  1302 + "geometry"=>json_decode($decoder->geomFromText($wkt)->toGeoJSON())
  1303 + );
  1304 + }
  1305 + $n[] = array (
  1306 + "type" => "FeatureCollection",
  1307 + "features" => $features
  1308 + );
  1309 + $contents = json_encode($n[0]);
  1310 + $contents = str_replace('\"','',$contents);
  1311 + file_put_contents($arq,$contents);
  1312 + ob_clean();
  1313 + header("Content-type: application/json; subtype=geojson");
  1314 + echo $contents;
  1315 + exit;
  1316 +}
  1317 +
1212 1318 function converteenc($texto){
1213 1319 if (!mb_detect_encoding($texto,"UTF-8",true)){
1214 1320 $texto = mb_convert_encoding($texto,"UTF-8","ISO-8859-1");
... ...
pacotes/gisconverter/copying.txt 0 → 100755
... ... @@ -0,0 +1,36 @@
  1 +gisconvert.php is available under the modified bsd license, and can be used and
  2 +redistributed under the conditions of this license. A copy of modified bsd
  3 +license is included in this file.
  4 +
  5 +===============================================================================
  6 +
  7 +Software License Agreement (BSD License)
  8 +
  9 +Copyright (c) 2010-2011, Arnaud Renevier
  10 +All rights reserved.
  11 +
  12 +Redistribution and use of this software in source and binary forms, with or without modification,
  13 +are permitted provided that the following conditions are met:
  14 +
  15 +* Redistributions of source code must retain the above
  16 + copyright notice, this list of conditions and the
  17 + following disclaimer.
  18 +
  19 +* Redistributions in binary form must reproduce the above
  20 + copyright notice, this list of conditions and the
  21 + following disclaimer in the documentation and/or other
  22 + materials provided with the distribution.
  23 +
  24 +* Neither the name of Arnaud Renevier nor the names of its
  25 + contributors may be used to endorse or promote products
  26 + derived from this software without specific prior
  27 + written permission of Parakey Inc.
  28 +
  29 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  30 +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  31 +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  32 +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  33 +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  34 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
  35 +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  36 +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
... ...
pacotes/gisconverter/example.php 0 → 100755
... ... @@ -0,0 +1,89 @@
  1 +#!/usr/bin/php
  2 +<?php
  3 +
  4 +// don't forget, you need php at least 5.3 for gisconverter.php
  5 +if (version_compare(PHP_VERSION, '5.3') < 0) {
  6 + die ('Sorry, you need php at least 5.3 for gisconverter.php');
  7 +}
  8 +
  9 +require ('gisconverter.php'); // first, include gisconverter.php library
  10 +
  11 +/*
  12 + * helper functions (see below)
  13 + */
  14 +function wkt_to_geojson ($text) {
  15 + $decoder = new gisconverter\WKT();
  16 + return $decoder->geomFromText($text)->toGeoJSON();
  17 +}
  18 +function wkt_to_kml ($text) {
  19 + $decoder = new gisconverter\WKT();
  20 + return $decoder->geomFromText($text)->toKML();
  21 +}
  22 +function wkt_to_gpx($text) {
  23 + $decoder = new gisconverter\WKT();
  24 + return $decoder->geomFromText($text)->toGPX();
  25 +}
  26 +function geojson_to_wkt ($text) {
  27 + $decoder = new gisconverter\GeoJSON();
  28 + return $decoder->geomFromText($text)->toWKT();
  29 +}
  30 +function geojson_to_kml ($text) {
  31 + $decoder = new gisconverter\GeoJSON();
  32 + return $decoder->geomFromText($text)->toKML();
  33 +}
  34 +function geojson_to_gpx ($text) {
  35 + $decoder = new gisconverter\GeoJSON();
  36 + return $decoder->geomFromText($text)->toGPX();
  37 +}
  38 +function kml_to_wkt ($text) {
  39 + $decoder = new gisconverter\KML();
  40 + return $decoder->geomFromText($text)->toWKT();
  41 +}
  42 +function kml_to_geojson ($text) {
  43 + $decoder = new gisconverter\KML();
  44 + return $decoder->geomFromText($text)->toGeoJSON();
  45 +}
  46 +function kml_to_gpx ($text) {
  47 + $decoder = new gisconverter\KML();
  48 + return $decoder->geomFromText($text)->toGPX();
  49 +}
  50 +function gpx_to_wkt ($text) {
  51 + $decoder = new gisconverter\GPX();
  52 + return $decoder->geomFromText($text)->toWKT();
  53 +}
  54 +function gpx_to_geojson ($text) {
  55 + $decoder = new gisconverter\GPX();
  56 + return $decoder->geomFromText($text)->toGeoJSON();
  57 +}
  58 +function gpx_to_kml ($text) {
  59 + $decoder = new gisconverter\GPX();
  60 + return $decoder->geomFromText($text)->toGPX();
  61 +}
  62 +
  63 +$decoder = new gisconverter\WKT(); # create a WKT decoder in gisconverter namespace
  64 +$geometry = $decoder->geomFromText('MULTIPOLYGON(((10 10,10 20,20 20,20 15,10 10)))'); # create a geometry from a given string input
  65 +
  66 +print $geometry->toGeoJSON(); # output geometry in GeoJSON format
  67 +print "\n\n";
  68 +
  69 +print $geometry->toKML(); # output geometry in KML format
  70 +print "\n\n";
  71 +
  72 +#ok, you get the idea. Now, let's use helper functions
  73 +
  74 +print geojson_to_kml('{"type":"LinearRing","coordinates":[[3.5,5.6],[4.8,10.5],[10,10],[3.5,5.6]]}');
  75 +print "\n\n";
  76 +
  77 +print geojson_to_wkt('{"type":"LinearRing","coordinates":[[3.5,5.6],[4.8,10.5],[10,10],[3.5,5.6]]}');
  78 +print "\n\n";
  79 +
  80 +print kml_to_wkt('<Polygon><outerBoundaryIs><LinearRing><coordinates>10,10 10,20 20,20 20,15 10,10</coordinates></LinearRing></outerBoundaryIs></Polygon>');
  81 +print "\n\n";
  82 +
  83 +print kml_to_geojson('<Polygon><outerBoundaryIs><LinearRing><coordinates>10,10 10,20 20,20 20,15 10,10</coordinates></LinearRing></outerBoundaryIs></Polygon>');
  84 +print "\n\n";
  85 +
  86 +print kml_to_gpx('<LineString><coordinates>3.5,5.6 4.8,10.5 10,10</coordinates></LineString>');
  87 +print "\n\n";
  88 +
  89 +?>
... ...
pacotes/gisconverter/gisconverter.php 0 → 100755
... ... @@ -0,0 +1,967 @@
  1 +<?php
  2 +
  3 +# Copyright (c) 2010-2011 Arnaud Renevier, Inc, published under the modified BSD
  4 +# license.
  5 +
  6 +namespace gisconverter;
  7 +
  8 +abstract class CustomException extends \Exception {
  9 + protected $message;
  10 + public function __toString() {
  11 + return get_class($this) . " {$this->message} in {$this->file}({$this->line})\n{$this->getTraceAsString()}";
  12 + }
  13 +}
  14 +
  15 +class Unimplemented extends CustomException {
  16 + public function __construct($message) {
  17 + $this->message = "unimplemented $message";
  18 + }
  19 +}
  20 +
  21 +class UnimplementedMethod extends Unimplemented {
  22 + public function __construct($method, $class) {
  23 + $this->message = "method {$this->class}::{$this->method}";
  24 + }
  25 +}
  26 +
  27 +class InvalidText extends CustomException {
  28 + public function __construct($decoder_name, $text = "") {
  29 + $this->message = "invalid text for decoder " . $decoder_name . ($text ? (": " . $text) : "");
  30 + }
  31 +}
  32 +
  33 +class InvalidFeature extends CustomException {
  34 + public function __construct($decoder_name, $text = "") {
  35 + $this->message = "invalid feature for decoder $decoder_name" . ($text ? ": $text" : "");
  36 + }
  37 +}
  38 +
  39 +abstract class OutOfRangeCoord extends CustomException {
  40 + private $coord;
  41 + public $type;
  42 +
  43 + public function __construct($coord) {
  44 + $this->message = "invalid {$this->type}: $coord";
  45 + }
  46 +}
  47 +class OutOfRangeLon extends outOfRangeCoord {
  48 + public $type = "longitude";
  49 +}
  50 +class OutOfRangeLat extends outOfRangeCoord {
  51 + public $type = "latitude";
  52 +}
  53 +
  54 +class UnavailableResource extends CustomException {
  55 + public function __construct($ressource) {
  56 + $this->message = "unavailable ressource: $ressource";
  57 + }
  58 +}
  59 +
  60 +interface iDecoder {
  61 + /*
  62 + * @param string $text
  63 + * @return Geometry
  64 + */
  65 + static public function geomFromText($text);
  66 +}
  67 +
  68 +abstract class Decoder implements iDecoder {
  69 + static public function geomFromText($text) {
  70 + throw new UnimplementedMethod(__FUNCTION__, get_called_class());
  71 + }
  72 +}
  73 +
  74 +interface iGeometry {
  75 + /*
  76 + * @return string
  77 + */
  78 + public function toGeoJSON();
  79 +
  80 + /*
  81 + * @return string
  82 + */
  83 + public function toKML();
  84 +
  85 + /*
  86 + * @return string
  87 + */
  88 + public function toWKT();
  89 +
  90 + /*
  91 + * @param mode: trkseg, rte or wpt
  92 + * @return string
  93 + */
  94 + public function toGPX($mode = null);
  95 +
  96 + /*
  97 + * @param Geometry $geom
  98 + * @return boolean
  99 + */
  100 + public function equals(Geometry $geom);
  101 +}
  102 +
  103 +abstract class Geometry implements iGeometry {
  104 + const name = "";
  105 +
  106 + public function toGeoJSON() {
  107 + throw new UnimplementedMethod(__FUNCTION__, get_called_class());
  108 + }
  109 +
  110 + public function toKML() {
  111 + throw new UnimplementedMethod(__FUNCTION__, get_called_class());
  112 + }
  113 +
  114 + public function toGPX($mode = null) {
  115 + throw new UnimplementedMethod(__FUNCTION__, get_called_class());
  116 + }
  117 +
  118 + public function toWKT() {
  119 + throw new UnimplementedMethod(__FUNCTION__, get_called_class());
  120 + }
  121 +
  122 + public function equals(Geometry $geom) {
  123 + throw new UnimplementedMethod(__FUNCTION__, get_called_class());
  124 + }
  125 +
  126 + public function __toString() {
  127 + return $this->toWKT();
  128 + }
  129 +}
  130 +
  131 +class WKT extends Decoder {
  132 + static public function geomFromText($text) {
  133 + $ltext = strtolower($text);
  134 + $type_pattern = '/\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/';
  135 + if (!preg_match($type_pattern, $ltext, $matches)) {
  136 + throw new InvalidText(__CLASS__, $text);
  137 + }
  138 + foreach (array("Point", "MultiPoint", "LineString", "MultiLinestring", "LinearRing",
  139 + "Polygon", "MultiPolygon", "GeometryCollection") as $wkt_type) {
  140 + if (strtolower($wkt_type) == $matches[1]) {
  141 + $type = $wkt_type;
  142 + break;
  143 + }
  144 + }
  145 +
  146 + if (!isset($type)) {
  147 + throw new InvalidText(__CLASS__, $text);
  148 + }
  149 +
  150 + try {
  151 + $components = call_user_func(array('static', 'parse' . $type), $matches[2]);
  152 + } catch(InvalidText $e) {
  153 + throw new InvalidText(__CLASS__, $text);
  154 + } catch(\Exception $e) {
  155 + throw $e;
  156 + }
  157 +
  158 + $constructor = __NAMESPACE__ . '\\' . $type;
  159 + return new $constructor($components);
  160 + }
  161 +
  162 + static protected function parsePoint($str) {
  163 + return preg_split('/\s+/', trim($str));
  164 + }
  165 +
  166 + static protected function parseMultiPoint($str) {
  167 + $str = trim($str);
  168 + if (strlen ($str) == 0) {
  169 + return array();
  170 + }
  171 + return static::parseLineString($str);
  172 + }
  173 +
  174 + static protected function parseLineString($str) {
  175 + $components = array();
  176 + foreach (preg_split('/,/', trim($str)) as $compstr) {
  177 + $components[] = new Point(static::parsePoint($compstr));
  178 + }
  179 + return $components;
  180 + }
  181 +
  182 + static protected function parseMultiLineString($str) {
  183 + return static::_parseCollection($str, "LineString");
  184 + }
  185 +
  186 + static protected function parseLinearRing($str) {
  187 + return static::parseLineString($str);
  188 + }
  189 +
  190 + static protected function parsePolygon($str) {
  191 + return static::_parseCollection($str, "LinearRing");
  192 + }
  193 +
  194 + static protected function parseMultiPolygon($str) {
  195 + return static::_parseCollection($str, "Polygon");
  196 + }
  197 +
  198 + static protected function parseGeometryCollection($str) {
  199 + $components = array();
  200 + foreach (preg_split('/,\s*(?=[A-Za-z])/', trim($str)) as $compstr) {
  201 + $components[] = static::geomFromText($compstr);
  202 + }
  203 + return $components;
  204 + }
  205 +
  206 + static protected function _parseCollection($str, $child_constructor) {
  207 + $components = array();
  208 + foreach (preg_split('/\)\s*,\s*\(/', trim($str)) as $compstr) {
  209 + if (strlen($compstr) and $compstr[0] == '(') {
  210 + $compstr = substr($compstr, 1);
  211 + }
  212 + if (strlen($compstr) and $compstr[strlen($compstr)-1] == ')') {
  213 + $compstr = substr($compstr, 0, -1);
  214 + }
  215 +
  216 + $childs = call_user_func(array('static', 'parse' . $child_constructor), $compstr);
  217 + $constructor = __NAMESPACE__ . '\\' . $child_constructor;
  218 + $components[] = new $constructor($childs);
  219 + }
  220 + return $components;
  221 + }
  222 +
  223 +}
  224 +
  225 +class GeoJSON extends Decoder {
  226 +
  227 + static public function geomFromText($text) {
  228 + $ltext = strtolower($text);
  229 + $obj = json_decode($ltext);
  230 + if (is_null ($obj)) {
  231 + throw new InvalidText(__CLASS__, $text);
  232 + }
  233 +
  234 + try {
  235 + $geom = static::_geomFromJson($obj);
  236 + } catch(InvalidText $e) {
  237 + throw new InvalidText(__CLASS__, $text);
  238 + } catch(\Exception $e) {
  239 + throw $e;
  240 + }
  241 +
  242 + return $geom;
  243 + }
  244 +
  245 + static protected function _geomFromJson($json) {
  246 + if (property_exists ($json, "geometry") and is_object($json->geometry)) {
  247 + return static::_geomFromJson($json->geometry);
  248 + }
  249 +
  250 + if (!property_exists ($json, "type") or !is_string($json->type)) {
  251 + throw new InvalidText(__CLASS__);
  252 + }
  253 +
  254 + foreach (array("Point", "MultiPoint", "LineString", "MultiLinestring", "LinearRing",
  255 + "Polygon", "MultiPolygon", "GeometryCollection") as $json_type) {
  256 + if (strtolower($json_type) == $json->type) {
  257 + $type = $json_type;
  258 + break;
  259 + }
  260 + }
  261 +
  262 + if (!isset($type)) {
  263 + throw new InvalidText(__CLASS__);
  264 + }
  265 +
  266 + try {
  267 + $components = call_user_func(array('static', 'parse'.$type), $json);
  268 + } catch(InvalidText $e) {
  269 + throw new InvalidText(__CLASS__);
  270 + } catch(\Exception $e) {
  271 + throw $e;
  272 + }
  273 +
  274 + $constructor = __NAMESPACE__ . '\\' . $type;
  275 + return new $constructor($components);
  276 + }
  277 +
  278 + static protected function parsePoint($json) {
  279 + if (!property_exists ($json, "coordinates") or !is_array($json->coordinates)) {
  280 + throw new InvalidText(__CLASS__);
  281 + }
  282 + return $json->coordinates;
  283 + }
  284 +
  285 + static protected function parseMultiPoint($json) {
  286 + if (!property_exists ($json, "coordinates") or !is_array($json->coordinates)) {
  287 + throw new InvalidText(__CLASS__);
  288 + }
  289 + return array_map(function($coords) {
  290 + return new Point($coords);
  291 + }, $json->coordinates);
  292 + }
  293 +
  294 + static protected function parseLineString($json) {
  295 + return static::parseMultiPoint($json);
  296 + }
  297 +
  298 + static protected function parseMultiLineString($json) {
  299 + $components = array();
  300 + if (!property_exists ($json, "coordinates") or !is_array($json->coordinates)) {
  301 + throw new InvalidText(__CLASS__);
  302 + }
  303 + foreach ($json->coordinates as $coordinates) {
  304 + $linecomp = array();
  305 + foreach ($coordinates as $coordinates) {
  306 + $linecomp[] = new Point($coordinates);
  307 + }
  308 + $components[] = new LineString($linecomp);
  309 + }
  310 + return $components;
  311 + }
  312 +
  313 + static protected function parseLinearRing($json) {
  314 + return static::parseMultiPoint($json);
  315 + }
  316 +
  317 + static protected function parsePolygon($json) {
  318 + $components = array();
  319 + if (!property_exists ($json, "coordinates") or !is_array($json->coordinates)) {
  320 + throw new InvalidText(__CLASS__);
  321 + }
  322 + foreach ($json->coordinates as $coordinates) {
  323 + $ringcomp = array();
  324 + foreach ($coordinates as $coordinates) {
  325 + $ringcomp[] = new Point($coordinates);
  326 + }
  327 + $components[] = new LinearRing($ringcomp);
  328 + }
  329 + return $components;
  330 + }
  331 +
  332 + static protected function parseMultiPolygon($json) {
  333 + $components = array();
  334 + if (!property_exists ($json, "coordinates") or !is_array($json->coordinates)) {
  335 + throw new InvalidText(__CLASS__);
  336 + }
  337 + foreach ($json->coordinates as $coordinates) {
  338 + $polycomp = array();
  339 + foreach ($coordinates as $coordinates) {
  340 + $ringcomp = array();
  341 + foreach ($coordinates as $coordinates) {
  342 + $ringcomp[] = new Point($coordinates);
  343 + }
  344 + $polycomp[] = new LinearRing($ringcomp);
  345 + }
  346 + $components[] = new Polygon($polycomp);
  347 + }
  348 + return $components;
  349 + }
  350 +
  351 + static protected function parseGeometryCollection($json) {
  352 + if (!property_exists ($json, "geometries") or !is_array($json->geometries)) {
  353 + throw new InvalidText(__CLASS__);
  354 + }
  355 + $components = array();
  356 + foreach ($json->geometries as $geometry) {
  357 + $components[] = static::_geomFromJson($geometry);
  358 + }
  359 +
  360 + return $components;
  361 + }
  362 +
  363 +}
  364 +
  365 +abstract class XML extends Decoder {
  366 + static public function geomFromText($text) {
  367 + if (!function_exists("simplexml_load_string") || !function_exists("libxml_use_internal_errors")) {
  368 + throw new UnavailableResource("simpleXML");
  369 + }
  370 + libxml_use_internal_errors(true);
  371 + $xmlobj = simplexml_load_string($text);
  372 + if ($xmlobj === false) {
  373 + throw new InvalidText(__CLASS__, $text);
  374 + }
  375 +
  376 + try {
  377 + $geom = static::_geomFromXML($xmlobj);
  378 + } catch(InvalidText $e) {
  379 + throw new InvalidText(__CLASS__, $text);
  380 + } catch(\Exception $e) {
  381 + throw $e;
  382 + }
  383 +
  384 + return $geom;
  385 + }
  386 +
  387 + static protected function childElements($xml, $nodename = "") {
  388 + $nodename = strtolower($nodename);
  389 + $res = array();
  390 + foreach ($xml->children() as $child) {
  391 + if ($nodename) {
  392 + if (strtolower($child->getName()) == $nodename) {
  393 + array_push($res, $child);
  394 + }
  395 + } else {
  396 + array_push($res, $child);
  397 + }
  398 + }
  399 + return $res;
  400 + }
  401 +
  402 + static protected function _childsCollect($xml) {
  403 + $components = array();
  404 + foreach (static::childElements($xml) as $child) {
  405 + try {
  406 + $geom = static::_geomFromXML($child);
  407 + $components[] = $geom;
  408 + } catch(InvalidText $e) {
  409 + }
  410 + }
  411 +
  412 + $ncomp = count($components);
  413 + if ($ncomp == 0) {
  414 + throw new InvalidText(__CLASS__);
  415 + } else if ($ncomp == 1) {
  416 + return $components[0];
  417 + } else {
  418 + return new GeometryCollection($components);
  419 + }
  420 + }
  421 +
  422 + protected static function _geomFromXML($xml) {}
  423 +}
  424 +
  425 +class KML extends XML {
  426 + static protected function parsePoint($xml) {
  427 + $coordinates = static::_extractCoordinates($xml);
  428 + $coords = preg_split('/,/', (string)$coordinates[0]);
  429 + return array_map("trim", $coords);
  430 + }
  431 +
  432 + static protected function parseLineString($xml) {
  433 + $coordinates = static::_extractCoordinates($xml);
  434 + foreach (preg_split('/\s+/', trim((string)$coordinates[0])) as $compstr) {
  435 + $coords = preg_split('/,/', $compstr);
  436 + $components[] = new Point($coords);
  437 + }
  438 + return $components;
  439 + }
  440 +
  441 + static protected function parseLinearRing($xml) {
  442 + return static::parseLineString($xml);
  443 + }
  444 +
  445 + static protected function parsePolygon($xml) {
  446 + $ring = array();
  447 + foreach (static::childElements($xml, 'outerboundaryis') as $elem) {
  448 + $ring = array_merge($ring, static::childElements($elem, 'linearring'));
  449 + }
  450 +
  451 + if (count($ring) != 1) {
  452 + throw new InvalidText(__CLASS__);
  453 + }
  454 +
  455 + $components = array(new LinearRing(static::parseLinearRing($ring[0])));
  456 + foreach (static::childElements($xml, 'innerboundaryis') as $elem) {
  457 + foreach (static::childElements($elem, 'linearring') as $ring) {
  458 + $components[] = new LinearRing(static::parseLinearRing($ring[0]));
  459 + }
  460 + }
  461 + return $components;
  462 + }
  463 +
  464 + static protected function parseMultiGeometry($xml) {
  465 + $components = array();
  466 + foreach ($xml->children() as $child) {
  467 + $components[] = static::_geomFromXML($child);
  468 + }
  469 + return $components;
  470 + }
  471 +
  472 + static protected function _extractCoordinates($xml) {
  473 + $coordinates = static::childElements($xml, 'coordinates');
  474 + if (count($coordinates) != 1) {
  475 + throw new InvalidText(__CLASS__);
  476 + }
  477 + return $coordinates;
  478 + }
  479 +
  480 + static protected function _geomFromXML($xml) {
  481 + $nodename = strtolower($xml->getName());
  482 + if ($nodename == "kml" or $nodename == "document" or $nodename == "placemark") {
  483 + return static::_childsCollect($xml);
  484 + }
  485 +
  486 + foreach (array("Point", "LineString", "LinearRing", "Polygon", "MultiGeometry") as $kml_type) {
  487 + if (strtolower($kml_type) == $nodename) {
  488 + $type = $kml_type;
  489 + break;
  490 + }
  491 + }
  492 +
  493 + if (!isset($type)) {
  494 + throw new InvalidText(__CLASS__);
  495 + }
  496 +
  497 + try {
  498 + $components = call_user_func(array('static', 'parse'.$type), $xml);
  499 + } catch(InvalidText $e) {
  500 + throw new InvalidText(__CLASS__);
  501 + } catch(\Exception $e) {
  502 + throw $e;
  503 + }
  504 +
  505 + if ($type == "MultiGeometry") {
  506 + if (count($components)) {
  507 + $possibletype = $components[0]::name;
  508 + $sametype = true;
  509 + foreach (array_slice($components, 1) as $component) {
  510 + if ($component::name != $possibletype) {
  511 + $sametype = false;
  512 + break;
  513 + }
  514 + }
  515 + if ($sametype) {
  516 + switch ($possibletype) {
  517 + case "Point":
  518 + return new MultiPoint($components);
  519 + break;
  520 + case "LineString":
  521 + return new MultiLineString($components);
  522 + break;
  523 + case "Polygon":
  524 + return new MultiPolygon($components);
  525 + break;
  526 + default:
  527 + break;
  528 + }
  529 + }
  530 + }
  531 + return new GeometryCollection($components);
  532 + }
  533 +
  534 + $constructor = __NAMESPACE__ . '\\' . $type;
  535 + return new $constructor($components);
  536 + }
  537 +}
  538 +
  539 +class GPX extends XML {
  540 + static protected function _extractCoordinates($xml) {
  541 + $attributes = $xml->attributes();
  542 + $lon = (string) $attributes['lon'];
  543 + $lat = (string) $attributes['lat'];
  544 + if (!$lon or !$lat) {
  545 + throw new InvalidText(__CLASS__);
  546 + }
  547 + return array($lon, $lat);
  548 + }
  549 +
  550 + static protected function parseTrkseg($xml) {
  551 + $res = array();
  552 + foreach ($xml->children() as $elem) {
  553 + if (strtolower($elem->getName()) == "trkpt") {
  554 + $res[] = new Point(static::_extractCoordinates($elem));
  555 + }
  556 + }
  557 + return $res;
  558 + }
  559 +
  560 + static protected function parseRte($xml) {
  561 + $res = array();
  562 + foreach ($xml->children() as $elem) {
  563 + if (strtolower($elem->getName()) == "rtept") {
  564 + $res[] = new Point(static::_extractCoordinates($elem));
  565 + }
  566 + }
  567 + return $res;
  568 + }
  569 +
  570 + static protected function parseWpt($xml) {
  571 + return static::_extractCoordinates($xml);
  572 + }
  573 +
  574 + static protected function _geomFromXML($xml) {
  575 + $nodename = strtolower($xml->getName());
  576 + if ($nodename == "gpx" or $nodename == "trk") {
  577 + return static::_childsCollect($xml);
  578 + }
  579 + foreach (array("Trkseg", "Rte", "Wpt") as $kml_type) {
  580 + if (strtolower($kml_type) == $xml->getName()) {
  581 + $type = $kml_type;
  582 + break;
  583 + }
  584 + }
  585 +
  586 + if (!isset($type)) {
  587 + throw new InvalidText(__CLASS__);
  588 + }
  589 +
  590 + try {
  591 + $components = call_user_func(array('static', 'parse'.$type), $xml);
  592 + } catch(InvalidText $e) {
  593 + throw new InvalidText(__CLASS__);
  594 + } catch(\Exception $e) {
  595 + throw $e;
  596 + }
  597 +
  598 + if ($type == "Trkseg" or $type == "Rte") {
  599 + $constructor = __NAMESPACE__ . '\\' . 'LineString';
  600 + } else if ($type == "Wpt") {
  601 + $constructor = __NAMESPACE__ . '\\' . 'Point';
  602 + }
  603 + return new $constructor($components);
  604 + }
  605 +}
  606 +
  607 +class Point extends Geometry {
  608 + const name = "Point";
  609 +
  610 + private $lon;
  611 + private $lat;
  612 +
  613 + public function __construct($coords) {
  614 + if (count ($coords) < 2) {
  615 + throw new InvalidFeature(__CLASS__, "Point must have two coordinates");
  616 + }
  617 + $lon = $coords[0];
  618 + $lat = $coords[1];
  619 + if (!$this->checkLon($lon)) {
  620 + throw new OutOfRangeLon($lon);
  621 + }
  622 + if (!$this->checkLat($lat)) {
  623 + throw new OutOfRangeLat($lat);
  624 + }
  625 + $this->lon = (float)$lon;
  626 + $this->lat = (float)$lat;
  627 + }
  628 +
  629 + public function __get($property) {
  630 + if ($property == "lon") {
  631 + return $this->lon;
  632 + } else if ($property == "lat") {
  633 + return $this->lat;
  634 + } else {
  635 + throw new \Exception ("Undefined property");
  636 + }
  637 + }
  638 +
  639 + public function toWKT() {
  640 + return strtoupper(static::name) . "({$this->lon} {$this->lat})";
  641 + }
  642 +
  643 + public function toKML() {
  644 + return "<" . static::name . "><coordinates>{$this->lon},{$this->lat}</coordinates></" . static::name . ">";
  645 + }
  646 +
  647 + public function toGPX($mode = null) {
  648 + if (!$mode) {
  649 + $mode = "wpt";
  650 + }
  651 + if ($mode != "wpt") {
  652 + throw new UnimplementedMethod(__FUNCTION__, get_called_class());
  653 + }
  654 + return "<wpt lon=\"{$this->lon}\" lat=\"{$this->lat}\"></wpt>";
  655 + }
  656 +
  657 + public function toGeoJSON() {
  658 + $value = (object)array ('type' => static::name, 'coordinates' => array($this->lon, $this->lat));
  659 + return json_encode($value);
  660 + }
  661 +
  662 + public function equals(Geometry $geom) {
  663 + if (get_class ($geom) != get_class($this)) {
  664 + return false;
  665 + }
  666 + return $geom->lat == $this->lat && $geom->lon == $this->lon;
  667 + }
  668 +
  669 + private function checkLon($lon) {
  670 + if (!is_numeric($lon)) {
  671 + return false;
  672 + }
  673 + if ($lon < -180 || $lon > 180) {
  674 + return false;
  675 + }
  676 + return true;
  677 + }
  678 + private function checkLat($lat) {
  679 + if (!is_numeric($lat)) {
  680 + return false;
  681 + }
  682 + if ($lat < -90 || $lat > 90) {
  683 + return false;
  684 + }
  685 + return true;
  686 + }
  687 +}
  688 +
  689 +abstract class Collection extends Geometry {
  690 + protected $components;
  691 +
  692 + public function __get($property) {
  693 + if ($property == "components") {
  694 + return $this->components;
  695 + } else {
  696 + throw new \Exception ("Undefined property");
  697 + }
  698 + }
  699 +
  700 + public function toWKT() {
  701 + $recursiveWKT = function ($geom) use (&$recursiveWKT) {
  702 + if ($geom instanceof Point) {
  703 + return "{$geom->lon} {$geom->lat}";
  704 + } else {
  705 + return "(" . implode (',', array_map($recursiveWKT, $geom->components)). ")";
  706 + }
  707 + };
  708 + return strtoupper(static::name) . call_user_func($recursiveWKT, $this);
  709 + }
  710 +
  711 + public function toGeoJSON() {
  712 + $recurviseJSON = function ($geom) use (&$recurviseJSON) {
  713 + if ($geom instanceof Point) {
  714 + return array($geom->lon, $geom->lat);
  715 + } else {
  716 + return array_map($recurviseJSON, $geom->components);
  717 + }
  718 + };
  719 + $value = (object)array ('type' => static::name, 'coordinates' => call_user_func($recurviseJSON, $this));
  720 + return json_encode($value);
  721 + }
  722 +
  723 + public function toKML() {
  724 + return '<MultiGeometry>' . implode("", array_map(function($comp) { return $comp->toKML(); }, $this->components)) . '</MultiGeometry>';
  725 + }
  726 +
  727 +}
  728 +
  729 +class MultiPoint extends Collection {
  730 + const name = "MultiPoint";
  731 +
  732 + public function __construct($components) {
  733 + foreach ($components as $comp) {
  734 + if (!($comp instanceof Point)) {
  735 + throw new InvalidFeature(__CLASS__, static::name . " can only contain Point elements");
  736 + }
  737 + }
  738 + $this->components = $components;
  739 + }
  740 +
  741 + public function equals(Geometry $geom) {
  742 + if (get_class ($geom) != get_class($this)) {
  743 + return false;
  744 + }
  745 + if (count($this->components) != count($geom->components)) {
  746 + return false;
  747 + }
  748 + foreach (range(0, count($this->components) - 1) as $count) {
  749 + if (!$this->components[$count]->equals($geom->components[$count])) {
  750 + return false;
  751 + }
  752 + }
  753 + return true;
  754 + }
  755 +
  756 +}
  757 +
  758 +class LineString extends MultiPoint {
  759 + const name = "LineString";
  760 + public function __construct($components) {
  761 + if (count ($components) < 2) {
  762 + throw new InvalidFeature(__CLASS__, "LineString must have at least 2 points");
  763 + }
  764 + parent::__construct($components);
  765 + }
  766 +
  767 + public function toKML() {
  768 + return "<" . static::name . "><coordinates>" . implode(" ", array_map(function($comp) {
  769 + return "{$comp->lon},{$comp->lat}";
  770 + }, $this->components)). "</coordinates></" . static::name . ">";
  771 + }
  772 +
  773 + public function toGPX($mode = null) {
  774 + if (!$mode) {
  775 + $mode = "trkseg";
  776 + }
  777 + if ($mode != "trkseg" and $mode != "rte") {
  778 + throw new UnimplementedMethod(__FUNCTION__, get_called_class());
  779 + }
  780 + if ($mode == "trkseg") {
  781 + return '<trkseg>' . implode ("", array_map(function ($comp) {
  782 + return "<trkpt lon=\"{$comp->lon}\" lat=\"{$comp->lat}\"></trkpt>";
  783 + }, $this->components)). "</trkseg>";
  784 + } else {
  785 + return '<rte>' . implode ("", array_map(function ($comp) {
  786 + return "<rtept lon=\"{$comp->lon}\" lat=\"{$comp->lat}\"></rtept>";
  787 + }, $this->components)). "</rte>";
  788 + }
  789 + }
  790 +
  791 +}
  792 +
  793 +class MultiLineString extends Collection {
  794 + const name = "MultiLineString";
  795 +
  796 + public function __construct($components) {
  797 + foreach ($components as $comp) {
  798 + if (!($comp instanceof LineString)) {
  799 + throw new InvalidFeature(__CLASS__, "MultiLineString can only contain LineString elements");
  800 + }
  801 + }
  802 + $this->components = $components;
  803 + }
  804 +
  805 +}
  806 +
  807 +class LinearRing extends LineString {
  808 + const name = "LinearRing";
  809 + public function __construct($components) {
  810 + $first = $components[0];
  811 + $last = end($components);
  812 + if (!$first->equals($last)) {
  813 + throw new InvalidFeature(__CLASS__, "LinearRing must be closed");
  814 + }
  815 + parent::__construct($components);
  816 + }
  817 + public function contains(Geometry $geom) {
  818 + if ($geom instanceof Collection) {
  819 + foreach ($geom->components as $point) {
  820 + if (!$this->contains($point)) {
  821 + return false;
  822 + }
  823 + }
  824 + return true;
  825 + } else if ($geom instanceof Point) {
  826 + return $this->containsPoint($geom);
  827 + } else {
  828 + throw new Unimplemented(get_class($this) . "::" . __FUNCTION__ . " for " . get_class($geom) . " geometry");
  829 + }
  830 + }
  831 +
  832 + protected function containsPoint(Point $point) {
  833 + /*
  834 + *PHP implementation of OpenLayers.Geometry.LinearRing.ContainsPoint algorithm
  835 + */
  836 + $px = round($point->lon, 14);
  837 + $py = round($point->lat, 14);
  838 +
  839 + $crosses = 0;
  840 + foreach (range(0, count($this->components) - 2) as $i) {
  841 + $start = $this->components[$i];
  842 + $x1 = round($start->lon, 14);
  843 + $y1 = round($start->lat, 14);
  844 + $end = $this->components[$i + 1];
  845 + $x2 = round($end->lon, 14);
  846 + $y2 = round($end->lat, 14);
  847 +
  848 + if($y1 == $y2) {
  849 + // horizontal edge
  850 + if($py == $y1) {
  851 + // point on horizontal line
  852 + if($x1 <= $x2 && ($px >= $x1 && $px <= $x2) || // right or vert
  853 + $x1 >= $x2 && ($px <= $x1 && $px >= $x2)) { // left or vert
  854 + // point on edge
  855 + $crosses = -1;
  856 + break;
  857 + }
  858 + }
  859 + // ignore other horizontal edges
  860 + continue;
  861 + }
  862 +
  863 + $cx = round(((($x1 - $x2) * $py) + (($x2 * $y1) - ($x1 * $y2))) / ($y1 - $y2), 14);
  864 +
  865 + if($cx == $px) {
  866 + // point on line
  867 + if($y1 < $y2 && ($py >= $y1 && $py <= $y2) || // upward
  868 + $y1 > $y2 && ($py <= $y1 && $py >= $y2)) { // downward
  869 + // point on edge
  870 + $crosses = -1;
  871 + break;
  872 + }
  873 + }
  874 + if($cx <= $px) {
  875 + // no crossing to the right
  876 + continue;
  877 + }
  878 + if($x1 != $x2 && ($cx < min($x1, $x2) || $cx > max($x1, $x2))) {
  879 + // no crossing
  880 + continue;
  881 + }
  882 + if($y1 < $y2 && ($py >= $y1 && $py < $y2) || // upward
  883 + $y1 > $y2 && ($py < $y1 && $py >= $y2)) { // downward
  884 + $crosses++;
  885 + }
  886 + }
  887 + $contained = ($crosses == -1) ?
  888 + // on edge
  889 + 1 :
  890 + // even (out) or odd (in)
  891 + !!($crosses & 1);
  892 +
  893 + return $contained;
  894 + }
  895 +
  896 +}
  897 +
  898 +class Polygon extends Collection {
  899 + const name = "Polygon";
  900 + public function __construct($components) {
  901 + $outer = $components[0];
  902 + foreach (array_slice($components, 1) as $inner) {
  903 + if (!$outer->contains($inner)) {
  904 + throw new InvalidFeature(__CLASS__, "Polygon inner rings must be enclosed in outer ring");
  905 + }
  906 + }
  907 + foreach ($components as $comp) {
  908 + if (!($comp instanceof LinearRing)) {
  909 + throw new InvalidFeature(__CLASS__, "Polygon can only contain LinearRing elements");
  910 + }
  911 + }
  912 + $this->components = $components;
  913 + }
  914 +
  915 + public function toKML() {
  916 + $str = '<outerBoundaryIs>' . $this->components[0]->toKML() . '</outerBoundaryIs>';
  917 + $str .= implode("", array_map(function($comp) {
  918 + return '<innerBoundaryIs>' . $comp->toKML() . '</innerBoundaryIs>';
  919 + }, array_slice($this->components, 1)));
  920 + return '<' . static::name . '>' . $str . '</' . static::name . '>';
  921 + }
  922 +
  923 +}
  924 +
  925 +class MultiPolygon extends Collection {
  926 + const name = "MultiPolygon";
  927 +
  928 + public function __construct($components) {
  929 + foreach ($components as $comp) {
  930 + if (!($comp instanceof Polygon)) {
  931 + throw new InvalidFeature(__CLASS__, "MultiPolygon can only contain Polygon elements");
  932 + }
  933 + }
  934 + $this->components = $components;
  935 + }
  936 +}
  937 +
  938 +class GeometryCollection extends Collection {
  939 + const name = "GeometryCollection";
  940 +
  941 + public function __construct($components) {
  942 + foreach ($components as $comp) {
  943 + if (!($comp instanceof Geometry)) {
  944 + throw new InvalidFeature(__CLASS__, "GeometryCollection can only contain Geometry elements");
  945 + }
  946 + }
  947 + $this->components = $components;
  948 + }
  949 +
  950 + public function toWKT() {
  951 + return strtoupper(static::name) . "(" . implode(',', array_map(function ($comp) {
  952 + return $comp->toWKT();
  953 + }, $this->components)) . ')';
  954 + }
  955 +
  956 + public function toGeoJSON() {
  957 + $value = (object)array ('type' => static::name, 'geometries' =>
  958 + array_map(function ($comp) {
  959 + // XXX: quite ugly
  960 + return json_decode($comp->toGeoJSON());
  961 + }, $this->components)
  962 + );
  963 + return json_encode($value);
  964 + }
  965 +}
  966 +
  967 +?>
... ...
pacotes/gisconverter/phpunit.xml 0 → 100755
... ... @@ -0,0 +1,11 @@
  1 +<phpunit bootstrap="./gisconverter.php"
  2 + colors="true"
  3 + convertErrorsToExceptions="true"
  4 + convertNoticesToExceptions="true"
  5 + convertWarningsToExceptions="true"
  6 + stopOnFailure="true"
  7 +>
  8 + <testsuite name="gisconverter">
  9 + <directory suffix=".php">./tests</directory>
  10 + </testsuite>
  11 +</phpunit>
... ...
pacotes/gisconverter/readme.txt 0 → 100755
... ... @@ -0,0 +1,8 @@
  1 +gisconverter.php is a php library to convert vector geometries between
  2 +different formats. Currently, WKT, GeoJSON, KML and GPX formats are supported.
  3 +See example.php for a documentation of how to use the library
  4 +
  5 +gisconverter.php needs php at least 5.3
  6 +
  7 +gisconverter.php is available under modified bsd license. See COPYING.txt for
  8 +more informations.
... ...