<?php
namespace GeoIO\WKT\Parser;
use GeoIO\Dimension;
use GeoIO\Factory;
use GeoIO\WKT\Parser\Exception\ParserException;
use JMS\Parser\AbstractParser;
class Parser extends AbstractParser
{
private $factory;
private $srid;
public function __construct(Factory $factory)
{
$this->factory = $factory;
parent::__construct(new Lexer());
}
public function parse($str, $context = null)
{
try {
return parent::parse($str, $context);
} catch (\Exception $e) {
throw new ParserException('Parsing failed: ' . $e->getMessage(), 0, $e);
}
}
protected function parseInternal()
{
$this->srid();
return $this->geometry();
}
private function srid()
{
$this->srid = null;
if ($this->lexer->isNext('SRID')) {
$this->match('SRID');
$this->match('=');
$this->srid = $this->match('INTEGER');
$this->match(';');
}
}
private function geometry(&$dimension = null)
{
$types = array(
'POINT',
'LINESTRING',
'POLYGON',
'MULTIPOINT',
'MULTILINESTRING',
'MULTIPOLYGON',
'GEOMETRYCOLLECTION',
);
switch ($this->matchAny($types)) {
case 'POINT':
return $this->point($dimension);
case 'LINESTRING':
return $this->lineString($dimension);
case 'POLYGON':
return $this->polygon($dimension);
case 'MULTIPOINT':
return $this->multiPoint($dimension);
case 'MULTILINESTRING':
return $this->multiLineString($dimension);
case 'MULTIPOLYGON':
return $this->multiPolygon($dimension);
default:
return $this->geometryCollection($dimension);
}
}
private function dimension($dimension)
{
if ((Dimension::DIMENSION_4D === $dimension || null === $dimension) &&
$this->lexer->isNext('ZM')) {
$this->match('ZM');
return Dimension::DIMENSION_4D;
}
if ((Dimension::DIMENSION_3DM === $dimension || null === $dimension) &&
$this->lexer->isNext('M')) {
$this->match('M');
return Dimension::DIMENSION_3DM;
}
if ((Dimension::DIMENSION_3DZ === $dimension || null === $dimension) &&
$this->lexer->isNext('Z')) {
$this->match('Z');
return Dimension::DIMENSION_3DZ;
}
return $dimension;
}
private function coordinates(&$dimension = null)
{
$coordinates = array(
'x' => $this->matchAny(array('FLOAT', 'INTEGER')),
'y' => $this->matchAny(array('FLOAT', 'INTEGER')),
'z' => null,
'm' => null
);
if (Dimension::DIMENSION_3DZ === $dimension ||
Dimension::DIMENSION_4D === $dimension ||
(null === $dimension && $this->lexer->isNextAny(array('FLOAT', 'INTEGER')))) {
$coordinates['z'] = $this->matchAny(array('FLOAT', 'INTEGER'));
}
if (Dimension::DIMENSION_3DM === $dimension ||
Dimension::DIMENSION_4D === $dimension ||
(null === $dimension && $this->lexer->isNextAny(array('FLOAT', 'INTEGER')))) {
$coordinates['m'] = $this->matchAny(array('FLOAT', 'INTEGER'));
}
if (null === $dimension) {
if (isset($coordinates['z']) && isset($coordinates['m'])) {
$dimension = Dimension::DIMENSION_4D;
} elseif (isset($coordinates['z'])) {
$dimension = Dimension::DIMENSION_3DZ;
}
}
return $this->factory->createPoint(
$dimension ?: Dimension::DIMENSION_2D,
$coordinates,
$this->srid
);
}
private function point(&$dimension = null)
{
$dimension = $this->dimension($dimension);
if ($this->lexer->isNext('EMPTY')) {
$this->match('EMPTY');
return $this->factory->createPoint(
$dimension ?: Dimension::DIMENSION_2D,
array(),
$this->srid
);
}
$this->match('(');
$point = $this->coordinates($dimension);
$this->match(')');
return $point;
}
private function lineStringText(&$dimension = null, $isLinearRing = false)
{
$this->match('(');
$points = array();
while (true) {
$points[] = $this->coordinates($dimension);
if (!$this->lexer->isNext(',')) {
break;
}
$this->match(',');
}
$this->match(')');
if ($isLinearRing) {
return $this->factory->createLinearRing(
$dimension ?: Dimension::DIMENSION_2D,
$points,
$this->srid
);
}
return $this->factory->createLineString(
$dimension ?: Dimension::DIMENSION_2D,
$points,
$this->srid
);
}
private function lineString(&$dimension = null)
{
$dimension = $this->dimension($dimension);
if ($this->lexer->isNext('EMPTY')) {
$this->match('EMPTY');
return $this->factory->createLineString(
$dimension ?: Dimension::DIMENSION_2D,
array(),
$this->srid
);
}
return $this->lineStringText($dimension);
}
private function polygonText(&$dimension = null)
{
$this->match('(');
$lineStrings = array();
while (true) {
$lineStrings[] = $this->lineStringText($dimension, true);
if (!$this->lexer->isNext(',')) {
break;
}
$this->match(',');
}
$this->match(')');
return $this->factory->createPolygon(
$dimension ?: Dimension::DIMENSION_2D,
$lineStrings,
$this->srid
);
}
private function polygon(&$dimension = null)
{
$dimension = $this->dimension($dimension);
if ($this->lexer->isNext('EMPTY')) {
$this->match('EMPTY');
return $this->factory->createPolygon(
$dimension ?: Dimension::DIMENSION_2D,
array(),
$this->srid
);
}
return $this->polygonText($dimension);
}
private function multiPoint(&$dimension = null)
{
$dimension = $this->dimension($dimension);
if ($this->lexer->isNext('EMPTY')) {
$this->match('EMPTY');
return $this->factory->createMultiPoint(
$dimension ?: Dimension::DIMENSION_2D,
array(),
$this->srid
);
}
$this->match('(');
$points = array();
while (true) {
$nonStandardPoint = true;
if ($this->lexer->isNext('(')) {
$this->match('(');
$nonStandardPoint = false;
}
$points[] = $this->coordinates($dimension);
if (!$nonStandardPoint) {
$this->match(')');
}
if (!$this->lexer->isNext(',')) {
break;
}
$this->match(',');
}
$this->match(')');
return $this->factory->createMultiPoint(
$dimension ?: Dimension::DIMENSION_2D,
$points,
$this->srid
);
}
private function multiLineString(&$dimension = null)
{
$dimension = $this->dimension($dimension);
if ($this->lexer->isNext('EMPTY')) {
$this->match('EMPTY');
return $this->factory->createMultiLineString(
$dimension ?: Dimension::DIMENSION_2D,
array(),
$this->srid
);
}
$this->match('(');
$lineStrings = array();
while (true) {
$lineStrings[] = $this->lineStringText($dimension);
if (!$this->lexer->isNext(',')) {
break;
}
$this->match(',');
}
$this->match(')');
return $this->factory->createMultiLineString(
$dimension ?: Dimension::DIMENSION_2D,
$lineStrings,
$this->srid
);
}
private function multiPolygon(&$dimension = null)
{
$dimension = $this->dimension($dimension);
if ($this->lexer->isNext('EMPTY')) {
$this->match('EMPTY');
return $this->factory->createMultiPolygon(
$dimension ?: Dimension::DIMENSION_2D,
array(),
$this->srid
);
}
$this->match('(');
$polygons = array();
while (true) {
$polygons[] = $this->polygonText($dimension);
if (!$this->lexer->isNext(',')) {
break;
}
$this->match(',');
}
$this->match(')');
return $this->factory->createMultiPolygon(
$dimension ?: Dimension::DIMENSION_2D,
$polygons,
$this->srid
);
}
private function geometryCollection(&$dimension = null)
{
$dimension = $this->dimension($dimension);
if ($this->lexer->isNext('EMPTY')) {
$this->match('EMPTY');
return $this->factory->createGeometryCollection(
$dimension ?: Dimension::DIMENSION_2D,
array(),
$this->srid
);
}
$this->match('(');
$geometries = array();
while (true) {
$geometries[] = $this->geometry($dimension);
if (!$this->lexer->isNext(',')) {
break;
}
$this->match(',');
}
$this->match(')');
return $this->factory->createGeometryCollection(
$dimension ?: Dimension::DIMENSION_2D,
$geometries,
$this->srid
);
}
}