Skip to content

Commit

Permalink
Merge pull request #10 from creof/release-2.2
Browse files Browse the repository at this point in the history
Release 2.2
  • Loading branch information
djlambert committed May 3, 2016
2 parents 0191764 + 6b458b1 commit bc4c051
Show file tree
Hide file tree
Showing 9 changed files with 619 additions and 306 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,30 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Added

### Changed

### Removed

## [2.2.0] - 2016-05-03
### Added
- Tests namespace to PSR-0 autoload in composer.json.
- Support for 3DZ, 3DM, 4DZM objects added. Dimension(s) now in 'dimension' key of returned array.

### Changed
- Token pattern regex changed to capture numbers in scientific notation as a single value.
- Let PHP handle scientific number conversion to float instead of manually calculating.
- Only instantiate Lexer object in Parser constructor if it doesn't exist.
- Move function return value tests from switch statement in Lexer. Switch statement now doing only string comparison.
- Remove static visibility from Lexer instance in Parser.
- PHPUnit now bootstraps Composer autoload.
- Update PHPUnit config XML to be compliant with XSD.
- Documentation updated for 3DZ, 3DM, 4DZM support.

### Removed
- Removed E token used with scientific notation from Lexer.
- Removed now unused TestInit.

## [2.1.0] - 2016-04-09
### Added
- Add tokens for 3DM, 3DZ, and 4D coordinates to Lexer.
Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![Test Coverage](https://codeclimate.com/github/creof/wkt-parser/badges/coverage.svg)](https://codeclimate.com/github/creof/wkt-parser/coverage)
[![Build Status](https://travis-ci.org/creof/wkt-parser.svg?branch=master)](https://travis-ci.org/creof/wkt-parser)

Lexer and parser library for WKT/EWKT spatial object strings.
Lexer and parser library for 2D, 3D, and 4D WKT/EWKT spatial object strings.

## Usage

Expand Down Expand Up @@ -33,11 +33,12 @@ $value2 = $parser->parse($input2);

## Return

The parser will return an array with the keys ```srid```, ```type```, and ```value```.
- ```type``` is the spatial object type (POINT, LINESTRING, etc.)
- ```value``` will contain an array with integer or float values for points, or nested arrays containing these based on spatial object type.
- ```srid``` is the SRID if EWKT value was parsed, null otherwise.
The parser will return an array with the keys ```type```, ```value```, ```srid```, and ```dimension```.
- ```type``` string, the spatial object type (POINT, LINESTRING, etc.) without any dimension.
- ```value``` array, contains integer or float values for points, or nested arrays containing these based on spatial object type.
- ```srid``` integer, the SRID if EWKT value was parsed, ```null``` otherwise.
- ```dimension``` string, will contain ```Z```, ```M```, or ```ZM``` for the respective 3D and 4D objects, ```null``` otherwise.

## Exceptions

The ```Lexer``` and ```Parser``` will throw expections implementing interface ```CrEOF\Geo\WKT\Exception\ExceptionInterface```.
The ```Lexer``` and ```Parser``` will throw exceptions implementing interface ```CrEOF\Geo\WKT\Exception\ExceptionInterface```.
7 changes: 6 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,17 @@
"doctrine/lexer": "~1.0"
},
"require-dev": {
"phpunit/phpunit": "<5.0",
"phpunit/phpunit": ">=4.8",
"codeclimate/php-test-reporter": "dev-master"
},
"autoload": {
"psr-0": {
"CrEOF\\Geo\\WKT": "lib/"
}
},
"autoload-dev": {
"psr-0": {
"CrEOF\\Geo\\WKT\\Tests": "tests/"
}
}
}
47 changes: 23 additions & 24 deletions lib/CrEOF/Geo/WKT/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ class Lexer extends AbstractLexer
const T_NONE = 1;
const T_INTEGER = 2;
const T_STRING = 3;
const T_E = 4;
const T_FLOAT = 5;
const T_CLOSE_PARENTHESIS = 6;
const T_OPEN_PARENTHESIS = 7;
Expand Down Expand Up @@ -95,36 +94,36 @@ public function value()
*/
protected function getType(&$value)
{
switch (true) {
case (is_numeric($value)):
if (strpos($value, '.') !== false) {
$value = (float) $value;
if (is_numeric($value)) {
$value += 0;

return self::T_FLOAT;
}
if (is_int($value)) {
return self::T_INTEGER;
}

$value = (int) $value;
return self::T_FLOAT;
}

return self::T_INTEGER;
case (strtoupper($value) === 'E'):
return self::T_E;
case (ctype_alpha($value)):
$name = __CLASS__ . '::T_' . strtoupper($value);
if (ctype_alpha($value)) {
$name = __CLASS__ . '::T_' . strtoupper($value);

if (defined($name)) {
return constant($name);
}
if (defined($name)) {
return constant($name);
}

return self::T_STRING;
}

return self::T_STRING;
case ($value === ','):
switch ($value) {
case ',':
return self::T_COMMA;
case ($value === '('):
case '(':
return self::T_OPEN_PARENTHESIS;
case ($value === ')'):
case ')':
return self::T_CLOSE_PARENTHESIS;
case ($value === '='):
case '=':
return self::T_EQUALS;
case ($value === ';'):
case ';':
return self::T_SEMICOLON;
default:
return self::T_NONE;
Expand All @@ -138,8 +137,8 @@ protected function getCatchablePatterns()
{
return array(
'',
'zm|[a-z]+[a-ln-z]',
'[+-]?[0-9]+(?:[\.][0-9]+)?'
'zm|[a-z]+[a-ln-y]',
'[+-]?[0-9]+(?:[\.][0-9]+)?(?:e[+-]?[0-9]+)?'
);
}

Expand Down
107 changes: 72 additions & 35 deletions lib/CrEOF/Geo/WKT/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,31 @@ class Parser
*/
private $srid;

/**
* @var string
*/
private $dimension;

/**
* @var Lexer
*/
private static $lexer;
private $lexer;

/**
* @param string $input
* @param string|null $input
*/
public function __construct($input = null)
{
self::$lexer = new Lexer();
$this->lexer = new Lexer();

if (null !== $input) {
$this->input = $input;
}
}

/**
* @param string|null $input
*
* @return array
*/
public function parse($input = null)
Expand All @@ -74,17 +81,19 @@ public function parse($input = null)
$this->input = $input;
}

self::$lexer->setInput($this->input);
self::$lexer->moveNext();
$this->lexer->setInput($this->input);
$this->lexer->moveNext();

$this->srid = null;
$this->srid = null;
$this->dimension = null;

if (self::$lexer->isNextToken(Lexer::T_SRID)) {
if ($this->lexer->isNextToken(Lexer::T_SRID)) {
$this->srid = $this->srid();
}

$geometry = $this->geometry();
$geometry['srid'] = $this->srid;
$geometry = $this->geometry();
$geometry['srid'] = $this->srid;
$geometry['dimension'] = '' === $this->dimension ? null : $this->dimension;

return $geometry;
}
Expand All @@ -100,7 +109,7 @@ protected function srid()
$this->match(Lexer::T_EQUALS);
$this->match(Lexer::T_INTEGER);

$srid = self::$lexer->value();
$srid = $this->lexer->value();

$this->match(Lexer::T_SEMICOLON);

Expand All @@ -116,7 +125,7 @@ protected function type()
{
$this->match(Lexer::T_TYPE);

return self::$lexer->value();
return $this->lexer->value();
}

/**
Expand All @@ -129,8 +138,10 @@ protected function geometry()
$type = $this->type();
$this->type = $type;

if (self::$lexer->isNextTokenAny(array(Lexer::T_Z, Lexer::T_M, Lexer::T_ZM))) {
$this->match(self::$lexer->lookahead['type']);
if ($this->lexer->isNextTokenAny(array(Lexer::T_Z, Lexer::T_M, Lexer::T_ZM))) {
$this->match($this->lexer->lookahead['type']);

$this->dimension = $this->lexer->value();
}

$this->match(Lexer::T_OPEN_PARENTHESIS);
Expand All @@ -152,31 +163,57 @@ protected function geometry()
*/
protected function point()
{
$x = $this->coordinate();
$y = $this->coordinate();
if (null !== $this->dimension) {
return $this->coordinates(2 + strlen($this->dimension));
}

return array($x, $y);
$values = $this->coordinates(2);

for ($i = 3; $i <= 4 && $this->lexer->isNextTokenAny(array(Lexer::T_FLOAT, Lexer::T_INTEGER)); $i++) {
$values[] = $this->coordinate();
}

switch (count($values)) {
case 2:
$this->dimension = '';
break;
case 3:
$this->dimension = 'Z';
break;
case 4:
$this->dimension = 'ZM';
break;
}

return $values;
}

/**
* Match a number and optional exponent
* @param int $count
*
* @return int|float
* @return array
*/
protected function coordinate()
protected function coordinates($count)
{
$this->match((self::$lexer->isNextToken(Lexer::T_FLOAT) ? Lexer::T_FLOAT : Lexer::T_INTEGER));
$values = array();

if (! self::$lexer->isNextToken(Lexer::T_E)) {
return self::$lexer->value();
for ($i = 1; $i <= $count; $i++) {
$values[] = $this->coordinate();
}

$number = self::$lexer->value();
return $values;
}

$this->match(Lexer::T_E);
$this->match(Lexer::T_INTEGER);
/**
* Match a number and optional exponent
*
* @return int|float
*/
protected function coordinate()
{
$this->match(($this->lexer->isNextToken(Lexer::T_FLOAT) ? Lexer::T_FLOAT : Lexer::T_INTEGER));

return $number * pow(10, self::$lexer->value());
return $this->lexer->value();
}

/**
Expand Down Expand Up @@ -208,7 +245,7 @@ protected function pointList()
{
$points = array($this->point());

while (self::$lexer->isNextToken(Lexer::T_COMMA)) {
while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);

$points[] = $this->point();
Expand All @@ -230,7 +267,7 @@ protected function pointLists()

$this->match(Lexer::T_CLOSE_PARENTHESIS);

while (self::$lexer->isNextToken(Lexer::T_COMMA)) {
while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);
$this->match(Lexer::T_OPEN_PARENTHESIS);

Expand All @@ -255,7 +292,7 @@ protected function multiPolygon()

$this->match(Lexer::T_CLOSE_PARENTHESIS);

while (self::$lexer->isNextToken(Lexer::T_COMMA)) {
while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);
$this->match(Lexer::T_OPEN_PARENTHESIS);

Expand Down Expand Up @@ -296,7 +333,7 @@ protected function geometryCollection()
{
$collection = array($this->geometry());

while (self::$lexer->isNextToken(Lexer::T_COMMA)) {
while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);

$collection[] = $this->geometry();
Expand All @@ -312,13 +349,13 @@ protected function geometryCollection()
*/
protected function match($token)
{
$lookaheadType = self::$lexer->lookahead['type'];
$lookaheadType = $this->lexer->lookahead['type'];

if ($lookaheadType !== $token && ($token !== Lexer::T_TYPE || $lookaheadType <= Lexer::T_TYPE)) {
throw $this->syntaxError(self::$lexer->getLiteral($token));
throw $this->syntaxError($this->lexer->getLiteral($token));
}

self::$lexer->moveNext();
$this->lexer->moveNext();
}

/**
Expand All @@ -331,8 +368,8 @@ protected function match($token)
private function syntaxError($expected)
{
$expected = sprintf('Expected %s, got', $expected);
$token = self::$lexer->lookahead;
$found = null === self::$lexer->lookahead ? 'end of string.' : sprintf('"%s"', $token['value']);
$token = $this->lexer->lookahead;
$found = null === $this->lexer->lookahead ? 'end of string.' : sprintf('"%s"', $token['value']);
$message = sprintf(
'[Syntax Error] line 0, col %d: Error: %s %s in value "%s"',
isset($token['position']) ? $token['position'] : '-1',
Expand Down
Loading

0 comments on commit bc4c051

Please sign in to comment.