From fe778f797fde7be21b291dcd923a91aa90c028bc Mon Sep 17 00:00:00 2001 From: Edmar Moretti Date: Wed, 27 May 2015 02:37:45 +0000 Subject: [PATCH] Adição da biblioteca gisconverter e conversor WFS para Geojson sem uso do OGR --- guia_de_migracao.txt | 2 ++ ms_configura.php | 6 ++++++ ogc.php | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- pacotes/gisconverter/copying.txt | 36 ++++++++++++++++++++++++++++++++++++ pacotes/gisconverter/example.php | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pacotes/gisconverter/gisconverter.php | 967 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pacotes/gisconverter/phpunit.xml | 11 +++++++++++ pacotes/gisconverter/readme.txt | 8 ++++++++ 8 files changed, 1235 insertions(+), 10 deletions(-) create mode 100755 pacotes/gisconverter/copying.txt create mode 100755 pacotes/gisconverter/example.php create mode 100755 pacotes/gisconverter/gisconverter.php create mode 100755 pacotes/gisconverter/phpunit.xml create mode 100755 pacotes/gisconverter/readme.txt diff --git a/guia_de_migracao.txt b/guia_de_migracao.txt index 8b67c0a..ac7a7ca 100644 --- a/guia_de_migracao.txt +++ b/guia_de_migracao.txt @@ -3,6 +3,8 @@ GUIA DE UPDATES e UPGRADES ------------------------------------------------------------------------------------------------- Para a versão 6.0 +- 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 + - para permitir o retorno da requisição WMS getfeatureinfo em JSON, foi incluído no mapfile base utilizado para gerar os serviços OGC a linha "wms_feature_info_mime_type" "application/json" em WEB->METADATA Caso você utilize um mapfile específico da sua instalação, veja o arquivo original existente em i3geo/aplicmap diff --git a/ms_configura.php b/ms_configura.php index f4a78b5..65a726b 100644 --- a/ms_configura.php +++ b/ms_configura.php @@ -74,6 +74,12 @@ $statusFerramentas = array( "melhorcaminho"=>true ); /* +Variable: $ogrOutput + +Indica se o OGR esta corretamente instalado, permitindo o seu uso nos servicos OGC de exportacao de dados +*/ +$ogrOutput = true; +/* Variable: saikuUrl URL para acessar o aplicativo SAIKU. Se nao estiver instalado, deixe em branco diff --git a/ogc.php b/ogc.php index f05115d..ed5eb3c 100644 --- a/ogc.php +++ b/ogc.php @@ -439,10 +439,8 @@ else{ if(!empty($parametro)){ $l->setfilter($parametro); $cache = false; - } - + } ms_newLayerObj($oMap, $l); - //$req->setParameter("LAYERS", "mundo"); } } } @@ -818,9 +816,6 @@ if(strtolower($req->getValueByName("REQUEST")) == "getfeatureinfo" && $_GET["inf getfeatureinfoJson(); exit; } -$oMap->owsdispatch($req); - -$contenttype = ms_iostripstdoutbuffercontenttype(); if(strtolower($request) == "getcapabilities"){ header('Content-Disposition: attachment; filename=getcapabilities.xml'); @@ -829,12 +824,19 @@ if(strtolower($request) == "getcapabilities"){ if(!isset($OUTPUTFORMAT)){ header("Content-type: $contenttype"); } +//$ogrOutput vem de ms_configura.php //precisa limpar o cabecalho if(strtolower($OUTPUTFORMAT) == "geojson" || strtolower($OUTPUTFORMAT) == "json"){ + $arq = $dir_tmp."/".$tema.".json"; + if(isset($ogrOutput) && $ogrOutput == false){ + exportaGeojson(); + exit; + } + $oMap->owsdispatch($req); + $contenttype = ms_iostripstdoutbuffercontenttype(); ms_iostripstdoutbuffercontentheaders(); //grava em disco - $arq = $dir_tmp."/".$tema.".json"; $contents = ms_iogetstdoutbufferstring(); file_put_contents($arq,$contents); //envia para download @@ -844,6 +846,8 @@ if(strtolower($OUTPUTFORMAT) == "geojson" || strtolower($OUTPUTFORMAT) == "json" exit; } if(strtolower($OUTPUTFORMAT) == "shape-zip"){ + $oMap->owsdispatch($req); + $contenttype = ms_iostripstdoutbuffercontenttype(); //grava em disco $arq = $dir_tmp."/".$tema."_shapefile.zip"; $contents = ms_iogetstdoutbufferstring(); @@ -855,18 +859,28 @@ if(strtolower($OUTPUTFORMAT) == "shape-zip"){ exit; } if(strtolower($OUTPUTFORMAT) == "csv"){ + $arq = $dir_tmp."/".$tema.$ows_geomtype.".csv"; + $fileName = $tema.$ows_geomtype.'.csv'; + if(isset($ogrOutput) && $ogrOutput == false){ + exportaCsv(); + exit; + } + // + $oMap->owsdispatch($req); + $contenttype = ms_iostripstdoutbuffercontenttype(); ms_iostripstdoutbuffercontentheaders(); //grava em disco - $arq = $dir_tmp."/".$tema.$ows_geomtype.".csv"; $contents = ms_iogetstdoutbufferstring(); file_put_contents($arq,$contents); //envia para download - header('Content-Disposition: attachment; filename='.$tema.$ows_geomtype.'.csv'); + header('Content-Disposition: attachment; filename='.$fileName); header("Content-type: text/csv"); ms_iogetStdoutBufferBytes(); ms_ioresethandlers(); exit; -} +} +$oMap->owsdispatch($req); +$contenttype = ms_iostripstdoutbuffercontenttype(); $buffer = ms_iogetStdoutBufferBytes(); ms_ioresethandlers(); @@ -1209,6 +1223,98 @@ function restauraMapaSalvo(){ $l = $m->getlayer(0); $_GET["LAYERS"] = $l->name; } +function exportaCsv(){ + global $oMap, $arq, $fileName, $versao, $ows_geomtype; + $layer = $oMap->getlayer(0); + $items = pegaItens($layer,$oMap); + $layer->querybyrect($oMap->extent); + $layer->open(); + $res_count = $layer->getNumresults(); + $linhas = array(); + if($ows_geomtype == "none"){ + $linhas[] = implode(",",$items); + } + else{ + $linhas[] = implode(",",$items).",wkt"; + } + for ($i = 0; $i < $res_count; $i++){ + if($versao == 6){ + $shape = $layer->getShape($layer->getResult($i)); + } + else{ + $shape = $layer->getFeature($layer->getResult($i)->shapeindex); + } + $reg = array(); + foreach ($items as $item){ + $v = trim($shape->values[$item]); + if(is_string($v)){ + $v = '"'.converteenc($v).'"'; + } + $reg[] = $v; + } + if($ows_geomtype != "none"){ + $reg[] = '"'.$shape->towkt().'"'; + } + $linhas[] = implode(",",$reg); + + } + $contents = implode("\n",$linhas); + file_put_contents($arq,$contents); + //envia para download + ob_clean(); + header('Content-Disposition: attachment; filename='.$fileName); + header("Content-type: text/csv"); + echo $contents; + exit; +} +function exportaGeojson(){ + global $oMap, $arq, $fileName, $versao, $ows_geomtype; + include("pacotes/gisconverter/gisconverter.php"); + $decoder = new gisconverter\WKT(); + $layer = $oMap->getlayer(0); + $items = pegaItens($layer,$oMap); + $layer->querybyrect($oMap->extent); + $layer->open(); + $res_count = $layer->getNumresults(); + $linhas = array(); + + $features = array(); + for ($i = 0; $i < $res_count; $i++){ + if($versao == 6){ + $shape = $layer->getShape($layer->getResult($i)); + } + else{ + $shape = $layer->getFeature($layer->getResult($i)->shapeindex); + } + $propriedades = array(); + foreach ($items as $item){ + $v = trim($shape->values[$item]); + if(is_string($v)){ + $v = '"'.converteenc($v).'"'; + } + $propriedades[] = array($item=>$v); + } + $wkt = $shape->towkt(); + + $features[] = array( + "type"=>"Feature", + "properties"=>$propriedades, + "geometry"=>json_decode($decoder->geomFromText($wkt)->toGeoJSON()) + ); + } + $n[] = array ( + "type" => "FeatureCollection", + "features" => $features + ); + $contents = json_encode($n[0]); + $contents = str_replace('\"','',$contents); + file_put_contents($arq,$contents); + ob_clean(); + header("Content-type: application/json; subtype=geojson"); + echo $contents; + exit; +} + function converteenc($texto){ if (!mb_detect_encoding($texto,"UTF-8",true)){ $texto = mb_convert_encoding($texto,"UTF-8","ISO-8859-1"); diff --git a/pacotes/gisconverter/copying.txt b/pacotes/gisconverter/copying.txt new file mode 100755 index 0000000..d4d62ea --- /dev/null +++ b/pacotes/gisconverter/copying.txt @@ -0,0 +1,36 @@ +gisconvert.php is available under the modified bsd license, and can be used and +redistributed under the conditions of this license. A copy of modified bsd +license is included in this file. + +=============================================================================== + +Software License Agreement (BSD License) + +Copyright (c) 2010-2011, Arnaud Renevier +All rights reserved. + +Redistribution and use of this software 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 Arnaud Renevier nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of Parakey Inc. + +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 OWNER OR +CONTRIBUTORS 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. diff --git a/pacotes/gisconverter/example.php b/pacotes/gisconverter/example.php new file mode 100755 index 0000000..cfe7eda --- /dev/null +++ b/pacotes/gisconverter/example.php @@ -0,0 +1,89 @@ +#!/usr/bin/php +geomFromText($text)->toGeoJSON(); +} +function wkt_to_kml ($text) { + $decoder = new gisconverter\WKT(); + return $decoder->geomFromText($text)->toKML(); +} +function wkt_to_gpx($text) { + $decoder = new gisconverter\WKT(); + return $decoder->geomFromText($text)->toGPX(); +} +function geojson_to_wkt ($text) { + $decoder = new gisconverter\GeoJSON(); + return $decoder->geomFromText($text)->toWKT(); +} +function geojson_to_kml ($text) { + $decoder = new gisconverter\GeoJSON(); + return $decoder->geomFromText($text)->toKML(); +} +function geojson_to_gpx ($text) { + $decoder = new gisconverter\GeoJSON(); + return $decoder->geomFromText($text)->toGPX(); +} +function kml_to_wkt ($text) { + $decoder = new gisconverter\KML(); + return $decoder->geomFromText($text)->toWKT(); +} +function kml_to_geojson ($text) { + $decoder = new gisconverter\KML(); + return $decoder->geomFromText($text)->toGeoJSON(); +} +function kml_to_gpx ($text) { + $decoder = new gisconverter\KML(); + return $decoder->geomFromText($text)->toGPX(); +} +function gpx_to_wkt ($text) { + $decoder = new gisconverter\GPX(); + return $decoder->geomFromText($text)->toWKT(); +} +function gpx_to_geojson ($text) { + $decoder = new gisconverter\GPX(); + return $decoder->geomFromText($text)->toGeoJSON(); +} +function gpx_to_kml ($text) { + $decoder = new gisconverter\GPX(); + return $decoder->geomFromText($text)->toGPX(); +} + +$decoder = new gisconverter\WKT(); # create a WKT decoder in gisconverter namespace +$geometry = $decoder->geomFromText('MULTIPOLYGON(((10 10,10 20,20 20,20 15,10 10)))'); # create a geometry from a given string input + +print $geometry->toGeoJSON(); # output geometry in GeoJSON format +print "\n\n"; + +print $geometry->toKML(); # output geometry in KML format +print "\n\n"; + +#ok, you get the idea. Now, let's use helper functions + +print geojson_to_kml('{"type":"LinearRing","coordinates":[[3.5,5.6],[4.8,10.5],[10,10],[3.5,5.6]]}'); +print "\n\n"; + +print geojson_to_wkt('{"type":"LinearRing","coordinates":[[3.5,5.6],[4.8,10.5],[10,10],[3.5,5.6]]}'); +print "\n\n"; + +print kml_to_wkt('10,10 10,20 20,20 20,15 10,10'); +print "\n\n"; + +print kml_to_geojson('10,10 10,20 20,20 20,15 10,10'); +print "\n\n"; + +print kml_to_gpx('3.5,5.6 4.8,10.5 10,10'); +print "\n\n"; + +?> diff --git a/pacotes/gisconverter/gisconverter.php b/pacotes/gisconverter/gisconverter.php new file mode 100755 index 0000000..9990125 --- /dev/null +++ b/pacotes/gisconverter/gisconverter.php @@ -0,0 +1,967 @@ +message} in {$this->file}({$this->line})\n{$this->getTraceAsString()}"; + } +} + +class Unimplemented extends CustomException { + public function __construct($message) { + $this->message = "unimplemented $message"; + } +} + +class UnimplementedMethod extends Unimplemented { + public function __construct($method, $class) { + $this->message = "method {$this->class}::{$this->method}"; + } +} + +class InvalidText extends CustomException { + public function __construct($decoder_name, $text = "") { + $this->message = "invalid text for decoder " . $decoder_name . ($text ? (": " . $text) : ""); + } +} + +class InvalidFeature extends CustomException { + public function __construct($decoder_name, $text = "") { + $this->message = "invalid feature for decoder $decoder_name" . ($text ? ": $text" : ""); + } +} + +abstract class OutOfRangeCoord extends CustomException { + private $coord; + public $type; + + public function __construct($coord) { + $this->message = "invalid {$this->type}: $coord"; + } +} +class OutOfRangeLon extends outOfRangeCoord { + public $type = "longitude"; +} +class OutOfRangeLat extends outOfRangeCoord { + public $type = "latitude"; +} + +class UnavailableResource extends CustomException { + public function __construct($ressource) { + $this->message = "unavailable ressource: $ressource"; + } +} + +interface iDecoder { + /* + * @param string $text + * @return Geometry + */ + static public function geomFromText($text); +} + +abstract class Decoder implements iDecoder { + static public function geomFromText($text) { + throw new UnimplementedMethod(__FUNCTION__, get_called_class()); + } +} + +interface iGeometry { + /* + * @return string + */ + public function toGeoJSON(); + + /* + * @return string + */ + public function toKML(); + + /* + * @return string + */ + public function toWKT(); + + /* + * @param mode: trkseg, rte or wpt + * @return string + */ + public function toGPX($mode = null); + + /* + * @param Geometry $geom + * @return boolean + */ + public function equals(Geometry $geom); +} + +abstract class Geometry implements iGeometry { + const name = ""; + + public function toGeoJSON() { + throw new UnimplementedMethod(__FUNCTION__, get_called_class()); + } + + public function toKML() { + throw new UnimplementedMethod(__FUNCTION__, get_called_class()); + } + + public function toGPX($mode = null) { + throw new UnimplementedMethod(__FUNCTION__, get_called_class()); + } + + public function toWKT() { + throw new UnimplementedMethod(__FUNCTION__, get_called_class()); + } + + public function equals(Geometry $geom) { + throw new UnimplementedMethod(__FUNCTION__, get_called_class()); + } + + public function __toString() { + return $this->toWKT(); + } +} + +class WKT extends Decoder { + static public function geomFromText($text) { + $ltext = strtolower($text); + $type_pattern = '/\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/'; + if (!preg_match($type_pattern, $ltext, $matches)) { + throw new InvalidText(__CLASS__, $text); + } + foreach (array("Point", "MultiPoint", "LineString", "MultiLinestring", "LinearRing", + "Polygon", "MultiPolygon", "GeometryCollection") as $wkt_type) { + if (strtolower($wkt_type) == $matches[1]) { + $type = $wkt_type; + break; + } + } + + if (!isset($type)) { + throw new InvalidText(__CLASS__, $text); + } + + try { + $components = call_user_func(array('static', 'parse' . $type), $matches[2]); + } catch(InvalidText $e) { + throw new InvalidText(__CLASS__, $text); + } catch(\Exception $e) { + throw $e; + } + + $constructor = __NAMESPACE__ . '\\' . $type; + return new $constructor($components); + } + + static protected function parsePoint($str) { + return preg_split('/\s+/', trim($str)); + } + + static protected function parseMultiPoint($str) { + $str = trim($str); + if (strlen ($str) == 0) { + return array(); + } + return static::parseLineString($str); + } + + static protected function parseLineString($str) { + $components = array(); + foreach (preg_split('/,/', trim($str)) as $compstr) { + $components[] = new Point(static::parsePoint($compstr)); + } + return $components; + } + + static protected function parseMultiLineString($str) { + return static::_parseCollection($str, "LineString"); + } + + static protected function parseLinearRing($str) { + return static::parseLineString($str); + } + + static protected function parsePolygon($str) { + return static::_parseCollection($str, "LinearRing"); + } + + static protected function parseMultiPolygon($str) { + return static::_parseCollection($str, "Polygon"); + } + + static protected function parseGeometryCollection($str) { + $components = array(); + foreach (preg_split('/,\s*(?=[A-Za-z])/', trim($str)) as $compstr) { + $components[] = static::geomFromText($compstr); + } + return $components; + } + + static protected function _parseCollection($str, $child_constructor) { + $components = array(); + foreach (preg_split('/\)\s*,\s*\(/', trim($str)) as $compstr) { + if (strlen($compstr) and $compstr[0] == '(') { + $compstr = substr($compstr, 1); + } + if (strlen($compstr) and $compstr[strlen($compstr)-1] == ')') { + $compstr = substr($compstr, 0, -1); + } + + $childs = call_user_func(array('static', 'parse' . $child_constructor), $compstr); + $constructor = __NAMESPACE__ . '\\' . $child_constructor; + $components[] = new $constructor($childs); + } + return $components; + } + +} + +class GeoJSON extends Decoder { + + static public function geomFromText($text) { + $ltext = strtolower($text); + $obj = json_decode($ltext); + if (is_null ($obj)) { + throw new InvalidText(__CLASS__, $text); + } + + try { + $geom = static::_geomFromJson($obj); + } catch(InvalidText $e) { + throw new InvalidText(__CLASS__, $text); + } catch(\Exception $e) { + throw $e; + } + + return $geom; + } + + static protected function _geomFromJson($json) { + if (property_exists ($json, "geometry") and is_object($json->geometry)) { + return static::_geomFromJson($json->geometry); + } + + if (!property_exists ($json, "type") or !is_string($json->type)) { + throw new InvalidText(__CLASS__); + } + + foreach (array("Point", "MultiPoint", "LineString", "MultiLinestring", "LinearRing", + "Polygon", "MultiPolygon", "GeometryCollection") as $json_type) { + if (strtolower($json_type) == $json->type) { + $type = $json_type; + break; + } + } + + if (!isset($type)) { + throw new InvalidText(__CLASS__); + } + + try { + $components = call_user_func(array('static', 'parse'.$type), $json); + } catch(InvalidText $e) { + throw new InvalidText(__CLASS__); + } catch(\Exception $e) { + throw $e; + } + + $constructor = __NAMESPACE__ . '\\' . $type; + return new $constructor($components); + } + + static protected function parsePoint($json) { + if (!property_exists ($json, "coordinates") or !is_array($json->coordinates)) { + throw new InvalidText(__CLASS__); + } + return $json->coordinates; + } + + static protected function parseMultiPoint($json) { + if (!property_exists ($json, "coordinates") or !is_array($json->coordinates)) { + throw new InvalidText(__CLASS__); + } + return array_map(function($coords) { + return new Point($coords); + }, $json->coordinates); + } + + static protected function parseLineString($json) { + return static::parseMultiPoint($json); + } + + static protected function parseMultiLineString($json) { + $components = array(); + if (!property_exists ($json, "coordinates") or !is_array($json->coordinates)) { + throw new InvalidText(__CLASS__); + } + foreach ($json->coordinates as $coordinates) { + $linecomp = array(); + foreach ($coordinates as $coordinates) { + $linecomp[] = new Point($coordinates); + } + $components[] = new LineString($linecomp); + } + return $components; + } + + static protected function parseLinearRing($json) { + return static::parseMultiPoint($json); + } + + static protected function parsePolygon($json) { + $components = array(); + if (!property_exists ($json, "coordinates") or !is_array($json->coordinates)) { + throw new InvalidText(__CLASS__); + } + foreach ($json->coordinates as $coordinates) { + $ringcomp = array(); + foreach ($coordinates as $coordinates) { + $ringcomp[] = new Point($coordinates); + } + $components[] = new LinearRing($ringcomp); + } + return $components; + } + + static protected function parseMultiPolygon($json) { + $components = array(); + if (!property_exists ($json, "coordinates") or !is_array($json->coordinates)) { + throw new InvalidText(__CLASS__); + } + foreach ($json->coordinates as $coordinates) { + $polycomp = array(); + foreach ($coordinates as $coordinates) { + $ringcomp = array(); + foreach ($coordinates as $coordinates) { + $ringcomp[] = new Point($coordinates); + } + $polycomp[] = new LinearRing($ringcomp); + } + $components[] = new Polygon($polycomp); + } + return $components; + } + + static protected function parseGeometryCollection($json) { + if (!property_exists ($json, "geometries") or !is_array($json->geometries)) { + throw new InvalidText(__CLASS__); + } + $components = array(); + foreach ($json->geometries as $geometry) { + $components[] = static::_geomFromJson($geometry); + } + + return $components; + } + +} + +abstract class XML extends Decoder { + static public function geomFromText($text) { + if (!function_exists("simplexml_load_string") || !function_exists("libxml_use_internal_errors")) { + throw new UnavailableResource("simpleXML"); + } + libxml_use_internal_errors(true); + $xmlobj = simplexml_load_string($text); + if ($xmlobj === false) { + throw new InvalidText(__CLASS__, $text); + } + + try { + $geom = static::_geomFromXML($xmlobj); + } catch(InvalidText $e) { + throw new InvalidText(__CLASS__, $text); + } catch(\Exception $e) { + throw $e; + } + + return $geom; + } + + static protected function childElements($xml, $nodename = "") { + $nodename = strtolower($nodename); + $res = array(); + foreach ($xml->children() as $child) { + if ($nodename) { + if (strtolower($child->getName()) == $nodename) { + array_push($res, $child); + } + } else { + array_push($res, $child); + } + } + return $res; + } + + static protected function _childsCollect($xml) { + $components = array(); + foreach (static::childElements($xml) as $child) { + try { + $geom = static::_geomFromXML($child); + $components[] = $geom; + } catch(InvalidText $e) { + } + } + + $ncomp = count($components); + if ($ncomp == 0) { + throw new InvalidText(__CLASS__); + } else if ($ncomp == 1) { + return $components[0]; + } else { + return new GeometryCollection($components); + } + } + + protected static function _geomFromXML($xml) {} +} + +class KML extends XML { + static protected function parsePoint($xml) { + $coordinates = static::_extractCoordinates($xml); + $coords = preg_split('/,/', (string)$coordinates[0]); + return array_map("trim", $coords); + } + + static protected function parseLineString($xml) { + $coordinates = static::_extractCoordinates($xml); + foreach (preg_split('/\s+/', trim((string)$coordinates[0])) as $compstr) { + $coords = preg_split('/,/', $compstr); + $components[] = new Point($coords); + } + return $components; + } + + static protected function parseLinearRing($xml) { + return static::parseLineString($xml); + } + + static protected function parsePolygon($xml) { + $ring = array(); + foreach (static::childElements($xml, 'outerboundaryis') as $elem) { + $ring = array_merge($ring, static::childElements($elem, 'linearring')); + } + + if (count($ring) != 1) { + throw new InvalidText(__CLASS__); + } + + $components = array(new LinearRing(static::parseLinearRing($ring[0]))); + foreach (static::childElements($xml, 'innerboundaryis') as $elem) { + foreach (static::childElements($elem, 'linearring') as $ring) { + $components[] = new LinearRing(static::parseLinearRing($ring[0])); + } + } + return $components; + } + + static protected function parseMultiGeometry($xml) { + $components = array(); + foreach ($xml->children() as $child) { + $components[] = static::_geomFromXML($child); + } + return $components; + } + + static protected function _extractCoordinates($xml) { + $coordinates = static::childElements($xml, 'coordinates'); + if (count($coordinates) != 1) { + throw new InvalidText(__CLASS__); + } + return $coordinates; + } + + static protected function _geomFromXML($xml) { + $nodename = strtolower($xml->getName()); + if ($nodename == "kml" or $nodename == "document" or $nodename == "placemark") { + return static::_childsCollect($xml); + } + + foreach (array("Point", "LineString", "LinearRing", "Polygon", "MultiGeometry") as $kml_type) { + if (strtolower($kml_type) == $nodename) { + $type = $kml_type; + break; + } + } + + if (!isset($type)) { + throw new InvalidText(__CLASS__); + } + + try { + $components = call_user_func(array('static', 'parse'.$type), $xml); + } catch(InvalidText $e) { + throw new InvalidText(__CLASS__); + } catch(\Exception $e) { + throw $e; + } + + if ($type == "MultiGeometry") { + if (count($components)) { + $possibletype = $components[0]::name; + $sametype = true; + foreach (array_slice($components, 1) as $component) { + if ($component::name != $possibletype) { + $sametype = false; + break; + } + } + if ($sametype) { + switch ($possibletype) { + case "Point": + return new MultiPoint($components); + break; + case "LineString": + return new MultiLineString($components); + break; + case "Polygon": + return new MultiPolygon($components); + break; + default: + break; + } + } + } + return new GeometryCollection($components); + } + + $constructor = __NAMESPACE__ . '\\' . $type; + return new $constructor($components); + } +} + +class GPX extends XML { + static protected function _extractCoordinates($xml) { + $attributes = $xml->attributes(); + $lon = (string) $attributes['lon']; + $lat = (string) $attributes['lat']; + if (!$lon or !$lat) { + throw new InvalidText(__CLASS__); + } + return array($lon, $lat); + } + + static protected function parseTrkseg($xml) { + $res = array(); + foreach ($xml->children() as $elem) { + if (strtolower($elem->getName()) == "trkpt") { + $res[] = new Point(static::_extractCoordinates($elem)); + } + } + return $res; + } + + static protected function parseRte($xml) { + $res = array(); + foreach ($xml->children() as $elem) { + if (strtolower($elem->getName()) == "rtept") { + $res[] = new Point(static::_extractCoordinates($elem)); + } + } + return $res; + } + + static protected function parseWpt($xml) { + return static::_extractCoordinates($xml); + } + + static protected function _geomFromXML($xml) { + $nodename = strtolower($xml->getName()); + if ($nodename == "gpx" or $nodename == "trk") { + return static::_childsCollect($xml); + } + foreach (array("Trkseg", "Rte", "Wpt") as $kml_type) { + if (strtolower($kml_type) == $xml->getName()) { + $type = $kml_type; + break; + } + } + + if (!isset($type)) { + throw new InvalidText(__CLASS__); + } + + try { + $components = call_user_func(array('static', 'parse'.$type), $xml); + } catch(InvalidText $e) { + throw new InvalidText(__CLASS__); + } catch(\Exception $e) { + throw $e; + } + + if ($type == "Trkseg" or $type == "Rte") { + $constructor = __NAMESPACE__ . '\\' . 'LineString'; + } else if ($type == "Wpt") { + $constructor = __NAMESPACE__ . '\\' . 'Point'; + } + return new $constructor($components); + } +} + +class Point extends Geometry { + const name = "Point"; + + private $lon; + private $lat; + + public function __construct($coords) { + if (count ($coords) < 2) { + throw new InvalidFeature(__CLASS__, "Point must have two coordinates"); + } + $lon = $coords[0]; + $lat = $coords[1]; + if (!$this->checkLon($lon)) { + throw new OutOfRangeLon($lon); + } + if (!$this->checkLat($lat)) { + throw new OutOfRangeLat($lat); + } + $this->lon = (float)$lon; + $this->lat = (float)$lat; + } + + public function __get($property) { + if ($property == "lon") { + return $this->lon; + } else if ($property == "lat") { + return $this->lat; + } else { + throw new \Exception ("Undefined property"); + } + } + + public function toWKT() { + return strtoupper(static::name) . "({$this->lon} {$this->lat})"; + } + + public function toKML() { + return "<" . static::name . ">{$this->lon},{$this->lat}"; + } + + public function toGPX($mode = null) { + if (!$mode) { + $mode = "wpt"; + } + if ($mode != "wpt") { + throw new UnimplementedMethod(__FUNCTION__, get_called_class()); + } + return "lon}\" lat=\"{$this->lat}\">"; + } + + public function toGeoJSON() { + $value = (object)array ('type' => static::name, 'coordinates' => array($this->lon, $this->lat)); + return json_encode($value); + } + + public function equals(Geometry $geom) { + if (get_class ($geom) != get_class($this)) { + return false; + } + return $geom->lat == $this->lat && $geom->lon == $this->lon; + } + + private function checkLon($lon) { + if (!is_numeric($lon)) { + return false; + } + if ($lon < -180 || $lon > 180) { + return false; + } + return true; + } + private function checkLat($lat) { + if (!is_numeric($lat)) { + return false; + } + if ($lat < -90 || $lat > 90) { + return false; + } + return true; + } +} + +abstract class Collection extends Geometry { + protected $components; + + public function __get($property) { + if ($property == "components") { + return $this->components; + } else { + throw new \Exception ("Undefined property"); + } + } + + public function toWKT() { + $recursiveWKT = function ($geom) use (&$recursiveWKT) { + if ($geom instanceof Point) { + return "{$geom->lon} {$geom->lat}"; + } else { + return "(" . implode (',', array_map($recursiveWKT, $geom->components)). ")"; + } + }; + return strtoupper(static::name) . call_user_func($recursiveWKT, $this); + } + + public function toGeoJSON() { + $recurviseJSON = function ($geom) use (&$recurviseJSON) { + if ($geom instanceof Point) { + return array($geom->lon, $geom->lat); + } else { + return array_map($recurviseJSON, $geom->components); + } + }; + $value = (object)array ('type' => static::name, 'coordinates' => call_user_func($recurviseJSON, $this)); + return json_encode($value); + } + + public function toKML() { + return '' . implode("", array_map(function($comp) { return $comp->toKML(); }, $this->components)) . ''; + } + +} + +class MultiPoint extends Collection { + const name = "MultiPoint"; + + public function __construct($components) { + foreach ($components as $comp) { + if (!($comp instanceof Point)) { + throw new InvalidFeature(__CLASS__, static::name . " can only contain Point elements"); + } + } + $this->components = $components; + } + + public function equals(Geometry $geom) { + if (get_class ($geom) != get_class($this)) { + return false; + } + if (count($this->components) != count($geom->components)) { + return false; + } + foreach (range(0, count($this->components) - 1) as $count) { + if (!$this->components[$count]->equals($geom->components[$count])) { + return false; + } + } + return true; + } + +} + +class LineString extends MultiPoint { + const name = "LineString"; + public function __construct($components) { + if (count ($components) < 2) { + throw new InvalidFeature(__CLASS__, "LineString must have at least 2 points"); + } + parent::__construct($components); + } + + public function toKML() { + return "<" . static::name . ">" . implode(" ", array_map(function($comp) { + return "{$comp->lon},{$comp->lat}"; + }, $this->components)). ""; + } + + public function toGPX($mode = null) { + if (!$mode) { + $mode = "trkseg"; + } + if ($mode != "trkseg" and $mode != "rte") { + throw new UnimplementedMethod(__FUNCTION__, get_called_class()); + } + if ($mode == "trkseg") { + return '' . implode ("", array_map(function ($comp) { + return "lon}\" lat=\"{$comp->lat}\">"; + }, $this->components)). ""; + } else { + return '' . implode ("", array_map(function ($comp) { + return "lon}\" lat=\"{$comp->lat}\">"; + }, $this->components)). ""; + } + } + +} + +class MultiLineString extends Collection { + const name = "MultiLineString"; + + public function __construct($components) { + foreach ($components as $comp) { + if (!($comp instanceof LineString)) { + throw new InvalidFeature(__CLASS__, "MultiLineString can only contain LineString elements"); + } + } + $this->components = $components; + } + +} + +class LinearRing extends LineString { + const name = "LinearRing"; + public function __construct($components) { + $first = $components[0]; + $last = end($components); + if (!$first->equals($last)) { + throw new InvalidFeature(__CLASS__, "LinearRing must be closed"); + } + parent::__construct($components); + } + public function contains(Geometry $geom) { + if ($geom instanceof Collection) { + foreach ($geom->components as $point) { + if (!$this->contains($point)) { + return false; + } + } + return true; + } else if ($geom instanceof Point) { + return $this->containsPoint($geom); + } else { + throw new Unimplemented(get_class($this) . "::" . __FUNCTION__ . " for " . get_class($geom) . " geometry"); + } + } + + protected function containsPoint(Point $point) { + /* + *PHP implementation of OpenLayers.Geometry.LinearRing.ContainsPoint algorithm + */ + $px = round($point->lon, 14); + $py = round($point->lat, 14); + + $crosses = 0; + foreach (range(0, count($this->components) - 2) as $i) { + $start = $this->components[$i]; + $x1 = round($start->lon, 14); + $y1 = round($start->lat, 14); + $end = $this->components[$i + 1]; + $x2 = round($end->lon, 14); + $y2 = round($end->lat, 14); + + if($y1 == $y2) { + // horizontal edge + if($py == $y1) { + // point on horizontal line + if($x1 <= $x2 && ($px >= $x1 && $px <= $x2) || // right or vert + $x1 >= $x2 && ($px <= $x1 && $px >= $x2)) { // left or vert + // point on edge + $crosses = -1; + break; + } + } + // ignore other horizontal edges + continue; + } + + $cx = round(((($x1 - $x2) * $py) + (($x2 * $y1) - ($x1 * $y2))) / ($y1 - $y2), 14); + + if($cx == $px) { + // point on line + if($y1 < $y2 && ($py >= $y1 && $py <= $y2) || // upward + $y1 > $y2 && ($py <= $y1 && $py >= $y2)) { // downward + // point on edge + $crosses = -1; + break; + } + } + if($cx <= $px) { + // no crossing to the right + continue; + } + if($x1 != $x2 && ($cx < min($x1, $x2) || $cx > max($x1, $x2))) { + // no crossing + continue; + } + if($y1 < $y2 && ($py >= $y1 && $py < $y2) || // upward + $y1 > $y2 && ($py < $y1 && $py >= $y2)) { // downward + $crosses++; + } + } + $contained = ($crosses == -1) ? + // on edge + 1 : + // even (out) or odd (in) + !!($crosses & 1); + + return $contained; + } + +} + +class Polygon extends Collection { + const name = "Polygon"; + public function __construct($components) { + $outer = $components[0]; + foreach (array_slice($components, 1) as $inner) { + if (!$outer->contains($inner)) { + throw new InvalidFeature(__CLASS__, "Polygon inner rings must be enclosed in outer ring"); + } + } + foreach ($components as $comp) { + if (!($comp instanceof LinearRing)) { + throw new InvalidFeature(__CLASS__, "Polygon can only contain LinearRing elements"); + } + } + $this->components = $components; + } + + public function toKML() { + $str = '' . $this->components[0]->toKML() . ''; + $str .= implode("", array_map(function($comp) { + return '' . $comp->toKML() . ''; + }, array_slice($this->components, 1))); + return '<' . static::name . '>' . $str . ''; + } + +} + +class MultiPolygon extends Collection { + const name = "MultiPolygon"; + + public function __construct($components) { + foreach ($components as $comp) { + if (!($comp instanceof Polygon)) { + throw new InvalidFeature(__CLASS__, "MultiPolygon can only contain Polygon elements"); + } + } + $this->components = $components; + } +} + +class GeometryCollection extends Collection { + const name = "GeometryCollection"; + + public function __construct($components) { + foreach ($components as $comp) { + if (!($comp instanceof Geometry)) { + throw new InvalidFeature(__CLASS__, "GeometryCollection can only contain Geometry elements"); + } + } + $this->components = $components; + } + + public function toWKT() { + return strtoupper(static::name) . "(" . implode(',', array_map(function ($comp) { + return $comp->toWKT(); + }, $this->components)) . ')'; + } + + public function toGeoJSON() { + $value = (object)array ('type' => static::name, 'geometries' => + array_map(function ($comp) { + // XXX: quite ugly + return json_decode($comp->toGeoJSON()); + }, $this->components) + ); + return json_encode($value); + } +} + +?> diff --git a/pacotes/gisconverter/phpunit.xml b/pacotes/gisconverter/phpunit.xml new file mode 100755 index 0000000..9064aa3 --- /dev/null +++ b/pacotes/gisconverter/phpunit.xml @@ -0,0 +1,11 @@ + + + ./tests + + diff --git a/pacotes/gisconverter/readme.txt b/pacotes/gisconverter/readme.txt new file mode 100755 index 0000000..13fd058 --- /dev/null +++ b/pacotes/gisconverter/readme.txt @@ -0,0 +1,8 @@ +gisconverter.php is a php library to convert vector geometries between +different formats. Currently, WKT, GeoJSON, KML and GPX formats are supported. +See example.php for a documentation of how to use the library + +gisconverter.php needs php at least 5.3 + +gisconverter.php is available under modified bsd license. See COPYING.txt for +more informations. -- libgit2 0.21.2