diff --git a/src/ol/format/WKT.js b/src/ol/format/WKT.js
index 504e9451d2..62b90c35a9 100644
--- a/src/ol/format/WKT.js
+++ b/src/ol/format/WKT.js
@@ -16,6 +16,509 @@ import Point from '../geom/Point.js';
import Polygon from '../geom/Polygon.js';
import SimpleGeometry from '../geom/SimpleGeometry.js';
+
+/**
+ * @const
+ * @type {string}
+ */
+const EMPTY = 'EMPTY';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+const Z = 'Z';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+const M = 'M';
+
+
+/**
+ * @const
+ * @type {string}
+ */
+const ZM = 'ZM';
+
+
+/**
+ * @const
+ * @enum {number}
+ */
+const TokenType = {
+ TEXT: 1,
+ LEFT_PAREN: 2,
+ RIGHT_PAREN: 3,
+ NUMBER: 4,
+ COMMA: 5,
+ EOF: 6
+};
+
+
+/**
+ * Class to tokenize a WKT string.
+ * @param {string} wkt WKT string.
+ * @constructor
+ */
+const Lexer = function(wkt) {
+
+ /**
+ * @type {string}
+ */
+ this.wkt = wkt;
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.index_ = -1;
+};
+
+
+/**
+ * @param {string} c Character.
+ * @return {boolean} Whether the character is alphabetic.
+ * @private
+ */
+Lexer.prototype.isAlpha_ = function(c) {
+ return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
+};
+
+
+/**
+ * @param {string} c Character.
+ * @param {boolean=} opt_decimal Whether the string number
+ * contains a dot, i.e. is a decimal number.
+ * @return {boolean} Whether the character is numeric.
+ * @private
+ */
+Lexer.prototype.isNumeric_ = function(c, opt_decimal) {
+ const decimal = opt_decimal !== undefined ? opt_decimal : false;
+ return c >= '0' && c <= '9' || c == '.' && !decimal;
+};
+
+
+/**
+ * @param {string} c Character.
+ * @return {boolean} Whether the character is whitespace.
+ * @private
+ */
+Lexer.prototype.isWhiteSpace_ = function(c) {
+ return c == ' ' || c == '\t' || c == '\r' || c == '\n';
+};
+
+
+/**
+ * @return {string} Next string character.
+ * @private
+ */
+Lexer.prototype.nextChar_ = function() {
+ return this.wkt.charAt(++this.index_);
+};
+
+
+/**
+ * Fetch and return the next token.
+ * @return {!ol.WKTToken} Next string token.
+ */
+Lexer.prototype.nextToken = function() {
+ const c = this.nextChar_();
+ const token = {position: this.index_, value: c};
+
+ if (c == '(') {
+ token.type = TokenType.LEFT_PAREN;
+ } else if (c == ',') {
+ token.type = TokenType.COMMA;
+ } else if (c == ')') {
+ token.type = TokenType.RIGHT_PAREN;
+ } else if (this.isNumeric_(c) || c == '-') {
+ token.type = TokenType.NUMBER;
+ token.value = this.readNumber_();
+ } else if (this.isAlpha_(c)) {
+ token.type = TokenType.TEXT;
+ token.value = this.readText_();
+ } else if (this.isWhiteSpace_(c)) {
+ return this.nextToken();
+ } else if (c === '') {
+ token.type = TokenType.EOF;
+ } else {
+ throw new Error('Unexpected character: ' + c);
+ }
+
+ return token;
+};
+
+
+/**
+ * @return {number} Numeric token value.
+ * @private
+ */
+Lexer.prototype.readNumber_ = function() {
+ let c;
+ const index = this.index_;
+ let decimal = false;
+ let scientificNotation = false;
+ do {
+ if (c == '.') {
+ decimal = true;
+ } else if (c == 'e' || c == 'E') {
+ scientificNotation = true;
+ }
+ c = this.nextChar_();
+ } while (
+ this.isNumeric_(c, decimal) ||
+ // if we haven't detected a scientific number before, 'e' or 'E'
+ // hint that we should continue to read
+ !scientificNotation && (c == 'e' || c == 'E') ||
+ // once we know that we have a scientific number, both '-' and '+'
+ // are allowed
+ scientificNotation && (c == '-' || c == '+')
+ );
+ return parseFloat(this.wkt.substring(index, this.index_--));
+};
+
+
+/**
+ * @return {string} String token value.
+ * @private
+ */
+Lexer.prototype.readText_ = function() {
+ let c;
+ const index = this.index_;
+ do {
+ c = this.nextChar_();
+ } while (this.isAlpha_(c));
+ return this.wkt.substring(index, this.index_--).toUpperCase();
+};
+
+
+/**
+ * Class to parse the tokens from the WKT string.
+ * @param {ol.format.Lexer} lexer The lexer.
+ * @constructor
+ */
+const Parser = function(lexer) {
+
+ /**
+ * @type {ol.format.Lexer}
+ * @private
+ */
+ this.lexer_ = lexer;
+
+ /**
+ * @type {ol.WKTToken}
+ * @private
+ */
+ this.token_;
+
+ /**
+ * @type {ol.geom.GeometryLayout}
+ * @private
+ */
+ this.layout_ = GeometryLayout.XY;
+};
+
+
+/**
+ * Fetch the next token form the lexer and replace the active token.
+ * @private
+ */
+Parser.prototype.consume_ = function() {
+ this.token_ = this.lexer_.nextToken();
+};
+
+/**
+ * Tests if the given type matches the type of the current token.
+ * @param {ol.format.TokenType} type Token type.
+ * @return {boolean} Whether the token matches the given type.
+ */
+Parser.prototype.isTokenType = function(type) {
+ const isMatch = this.token_.type == type;
+ return isMatch;
+};
+
+
+/**
+ * If the given type matches the current token, consume it.
+ * @param {ol.format.TokenType} type Token type.
+ * @return {boolean} Whether the token matches the given type.
+ */
+Parser.prototype.match = function(type) {
+ const isMatch = this.isTokenType(type);
+ if (isMatch) {
+ this.consume_();
+ }
+ return isMatch;
+};
+
+
+/**
+ * Try to parse the tokens provided by the lexer.
+ * @return {ol.geom.Geometry} The geometry.
+ */
+Parser.prototype.parse = function() {
+ this.consume_();
+ const geometry = this.parseGeometry_();
+ return geometry;
+};
+
+
+/**
+ * Try to parse the dimensional info.
+ * @return {ol.geom.GeometryLayout} The layout.
+ * @private
+ */
+Parser.prototype.parseGeometryLayout_ = function() {
+ let layout = GeometryLayout.XY;
+ const dimToken = this.token_;
+ if (this.isTokenType(TokenType.TEXT)) {
+ const dimInfo = dimToken.value;
+ if (dimInfo === Z) {
+ layout = GeometryLayout.XYZ;
+ } else if (dimInfo === M) {
+ layout = GeometryLayout.XYM;
+ } else if (dimInfo === ZM) {
+ layout = GeometryLayout.XYZM;
+ }
+ if (layout !== GeometryLayout.XY) {
+ this.consume_();
+ }
+ }
+ return layout;
+};
+
+
+/**
+ * @return {!Array.
} A collection of geometries.
+ * @private
+ */
+Parser.prototype.parseGeometryCollectionText_ = function() {
+ if (this.match(TokenType.LEFT_PAREN)) {
+ const geometries = [];
+ do {
+ geometries.push(this.parseGeometry_());
+ } while (this.match(TokenType.COMMA));
+ if (this.match(TokenType.RIGHT_PAREN)) {
+ return geometries;
+ }
+ } else if (this.isEmptyGeometry_()) {
+ return [];
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {Array.} All values in a point.
+ * @private
+ */
+Parser.prototype.parsePointText_ = function() {
+ if (this.match(TokenType.LEFT_PAREN)) {
+ const coordinates = this.parsePoint_();
+ if (this.match(TokenType.RIGHT_PAREN)) {
+ return coordinates;
+ }
+ } else if (this.isEmptyGeometry_()) {
+ return null;
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {!Array.>} All points in a linestring.
+ * @private
+ */
+Parser.prototype.parseLineStringText_ = function() {
+ if (this.match(TokenType.LEFT_PAREN)) {
+ const coordinates = this.parsePointList_();
+ if (this.match(TokenType.RIGHT_PAREN)) {
+ return coordinates;
+ }
+ } else if (this.isEmptyGeometry_()) {
+ return [];
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {!Array.>} All points in a polygon.
+ * @private
+ */
+Parser.prototype.parsePolygonText_ = function() {
+ if (this.match(TokenType.LEFT_PAREN)) {
+ const coordinates = this.parseLineStringTextList_();
+ if (this.match(TokenType.RIGHT_PAREN)) {
+ return coordinates;
+ }
+ } else if (this.isEmptyGeometry_()) {
+ return [];
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {!Array.>} All points in a multipoint.
+ * @private
+ */
+Parser.prototype.parseMultiPointText_ = function() {
+ if (this.match(TokenType.LEFT_PAREN)) {
+ let coordinates;
+ if (this.token_.type == TokenType.LEFT_PAREN) {
+ coordinates = this.parsePointTextList_();
+ } else {
+ coordinates = this.parsePointList_();
+ }
+ if (this.match(TokenType.RIGHT_PAREN)) {
+ return coordinates;
+ }
+ } else if (this.isEmptyGeometry_()) {
+ return [];
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {!Array.>} All linestring points
+ * in a multilinestring.
+ * @private
+ */
+Parser.prototype.parseMultiLineStringText_ = function() {
+ if (this.match(TokenType.LEFT_PAREN)) {
+ const coordinates = this.parseLineStringTextList_();
+ if (this.match(TokenType.RIGHT_PAREN)) {
+ return coordinates;
+ }
+ } else if (this.isEmptyGeometry_()) {
+ return [];
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {!Array.>} All polygon points in a multipolygon.
+ * @private
+ */
+Parser.prototype.parseMultiPolygonText_ = function() {
+ if (this.match(TokenType.LEFT_PAREN)) {
+ const coordinates = this.parsePolygonTextList_();
+ if (this.match(TokenType.RIGHT_PAREN)) {
+ return coordinates;
+ }
+ } else if (this.isEmptyGeometry_()) {
+ return [];
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {!Array.} A point.
+ * @private
+ */
+Parser.prototype.parsePoint_ = function() {
+ const coordinates = [];
+ const dimensions = this.layout_.length;
+ for (let i = 0; i < dimensions; ++i) {
+ const token = this.token_;
+ if (this.match(TokenType.NUMBER)) {
+ coordinates.push(token.value);
+ } else {
+ break;
+ }
+ }
+ if (coordinates.length == dimensions) {
+ return coordinates;
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
+/**
+ * @return {!Array.>} An array of points.
+ * @private
+ */
+Parser.prototype.parsePointList_ = function() {
+ const coordinates = [this.parsePoint_()];
+ while (this.match(TokenType.COMMA)) {
+ coordinates.push(this.parsePoint_());
+ }
+ return coordinates;
+};
+
+
+/**
+ * @return {!Array.>} An array of points.
+ * @private
+ */
+Parser.prototype.parsePointTextList_ = function() {
+ const coordinates = [this.parsePointText_()];
+ while (this.match(TokenType.COMMA)) {
+ coordinates.push(this.parsePointText_());
+ }
+ return coordinates;
+};
+
+
+/**
+ * @return {!Array.>} An array of points.
+ * @private
+ */
+Parser.prototype.parseLineStringTextList_ = function() {
+ const coordinates = [this.parseLineStringText_()];
+ while (this.match(TokenType.COMMA)) {
+ coordinates.push(this.parseLineStringText_());
+ }
+ return coordinates;
+};
+
+
+/**
+ * @return {!Array.>} An array of points.
+ * @private
+ */
+Parser.prototype.parsePolygonTextList_ = function() {
+ const coordinates = [this.parsePolygonText_()];
+ while (this.match(TokenType.COMMA)) {
+ coordinates.push(this.parsePolygonText_());
+ }
+ return coordinates;
+};
+
+
+/**
+ * @return {boolean} Whether the token implies an empty geometry.
+ * @private
+ */
+Parser.prototype.isEmptyGeometry_ = function() {
+ const isEmpty = this.isTokenType(TokenType.TEXT) &&
+ this.token_.value == EMPTY;
+ if (isEmpty) {
+ this.consume_();
+ }
+ return isEmpty;
+};
+
+
+/**
+ * Create an error message for an unexpected token error.
+ * @return {string} Error message.
+ * @private
+ */
+Parser.prototype.formatErrorMessage_ = function() {
+ return 'Unexpected `' + this.token_.value + '` at position ' +
+ this.token_.position + ' in `' + this.lexer_.wkt + '`';
+};
+
+
/**
* @classdesc
* Geometry format for reading and writing data in the `WellKnownText` (WKT)
@@ -45,155 +548,134 @@ const WKT = function(opt_options) {
inherits(WKT, TextFeature);
-/**
- * @const
- * @type {string}
- */
-WKT.EMPTY = 'EMPTY';
-
-
-/**
- * @const
- * @type {string}
- */
-WKT.Z = 'Z';
-
-
-/**
- * @const
- * @type {string}
- */
-WKT.M = 'M';
-
-
-/**
- * @const
- * @type {string}
- */
-WKT.ZM = 'ZM';
-
-
/**
* @param {ol.geom.Point} geom Point geometry.
* @return {string} Coordinates part of Point as WKT.
- * @private
*/
-WKT.encodePointGeometry_ = function(geom) {
+function encodePointGeometry(geom) {
const coordinates = geom.getCoordinates();
if (coordinates.length === 0) {
return '';
}
return coordinates.join(' ');
-};
+}
/**
* @param {ol.geom.MultiPoint} geom MultiPoint geometry.
* @return {string} Coordinates part of MultiPoint as WKT.
- * @private
*/
-WKT.encodeMultiPointGeometry_ = function(geom) {
+function encodeMultiPointGeometry(geom) {
const array = [];
const components = geom.getPoints();
for (let i = 0, ii = components.length; i < ii; ++i) {
- array.push('(' + WKT.encodePointGeometry_(components[i]) + ')');
+ array.push('(' + encodePointGeometry(components[i]) + ')');
}
return array.join(',');
-};
+}
/**
* @param {ol.geom.GeometryCollection} geom GeometryCollection geometry.
* @return {string} Coordinates part of GeometryCollection as WKT.
- * @private
*/
-WKT.encodeGeometryCollectionGeometry_ = function(geom) {
+function encodeGeometryCollectionGeometry(geom) {
const array = [];
const geoms = geom.getGeometries();
for (let i = 0, ii = geoms.length; i < ii; ++i) {
- array.push(WKT.encode_(geoms[i]));
+ array.push(encode(geoms[i]));
}
return array.join(',');
-};
+}
/**
* @param {ol.geom.LineString|ol.geom.LinearRing} geom LineString geometry.
* @return {string} Coordinates part of LineString as WKT.
- * @private
*/
-WKT.encodeLineStringGeometry_ = function(geom) {
+function encodeLineStringGeometry(geom) {
const coordinates = geom.getCoordinates();
const array = [];
for (let i = 0, ii = coordinates.length; i < ii; ++i) {
array.push(coordinates[i].join(' '));
}
return array.join(',');
-};
+}
/**
* @param {ol.geom.MultiLineString} geom MultiLineString geometry.
* @return {string} Coordinates part of MultiLineString as WKT.
- * @private
*/
-WKT.encodeMultiLineStringGeometry_ = function(geom) {
+function encodeMultiLineStringGeometry(geom) {
const array = [];
const components = geom.getLineStrings();
for (let i = 0, ii = components.length; i < ii; ++i) {
- array.push('(' + WKT.encodeLineStringGeometry_(
+ array.push('(' + encodeLineStringGeometry(
components[i]) + ')');
}
return array.join(',');
-};
+}
/**
* @param {ol.geom.Polygon} geom Polygon geometry.
* @return {string} Coordinates part of Polygon as WKT.
- * @private
*/
-WKT.encodePolygonGeometry_ = function(geom) {
+function encodePolygonGeometry(geom) {
const array = [];
const rings = geom.getLinearRings();
for (let i = 0, ii = rings.length; i < ii; ++i) {
- array.push('(' + WKT.encodeLineStringGeometry_(
+ array.push('(' + encodeLineStringGeometry(
rings[i]) + ')');
}
return array.join(',');
-};
+}
/**
* @param {ol.geom.MultiPolygon} geom MultiPolygon geometry.
* @return {string} Coordinates part of MultiPolygon as WKT.
- * @private
*/
-WKT.encodeMultiPolygonGeometry_ = function(geom) {
+function encodeMultiPolygonGeometry(geom) {
const array = [];
const components = geom.getPolygons();
for (let i = 0, ii = components.length; i < ii; ++i) {
- array.push('(' + WKT.encodePolygonGeometry_(
+ array.push('(' + encodePolygonGeometry(
components[i]) + ')');
}
return array.join(',');
-};
+}
/**
* @param {ol.geom.SimpleGeometry} geom SimpleGeometry geometry.
* @return {string} Potential dimensional information for WKT type.
- * @private
*/
-WKT.encodeGeometryLayout_ = function(geom) {
+function encodeGeometryLayout(geom) {
const layout = geom.getLayout();
let dimInfo = '';
if (layout === GeometryLayout.XYZ || layout === GeometryLayout.XYZM) {
- dimInfo += WKT.Z;
+ dimInfo += Z;
}
if (layout === GeometryLayout.XYM || layout === GeometryLayout.XYZM) {
- dimInfo += WKT.M;
+ dimInfo += M;
}
return dimInfo;
+}
+
+
+/**
+ * @const
+ * @type {Object.}
+ */
+const GeometryEncoder = {
+ 'Point': encodePointGeometry,
+ 'LineString': encodeLineStringGeometry,
+ 'Polygon': encodePolygonGeometry,
+ 'MultiPoint': encodeMultiPointGeometry,
+ 'MultiLineString': encodeMultiLineStringGeometry,
+ 'MultiPolygon': encodeMultiPolygonGeometry,
+ 'GeometryCollection': encodeGeometryCollectionGeometry
};
@@ -201,40 +683,23 @@ WKT.encodeGeometryLayout_ = function(geom) {
* Encode a geometry as WKT.
* @param {ol.geom.Geometry} geom The geometry to encode.
* @return {string} WKT string for the geometry.
- * @private
*/
-WKT.encode_ = function(geom) {
+function encode(geom) {
let type = geom.getType();
- const geometryEncoder = WKT.GeometryEncoder_[type];
+ const geometryEncoder = GeometryEncoder[type];
const enc = geometryEncoder(geom);
type = type.toUpperCase();
if (geom instanceof SimpleGeometry) {
- const dimInfo = WKT.encodeGeometryLayout_(geom);
+ const dimInfo = encodeGeometryLayout(geom);
if (dimInfo.length > 0) {
type += ' ' + dimInfo;
}
}
if (enc.length === 0) {
- return type + ' ' + WKT.EMPTY;
+ return type + ' ' + EMPTY;
}
return type + '(' + enc + ')';
-};
-
-
-/**
- * @const
- * @type {Object.}
- * @private
- */
-WKT.GeometryEncoder_ = {
- 'Point': WKT.encodePointGeometry_,
- 'LineString': WKT.encodeLineStringGeometry_,
- 'Polygon': WKT.encodePolygonGeometry_,
- 'MultiPoint': WKT.encodeMultiPointGeometry_,
- 'MultiLineString': WKT.encodeMultiLineStringGeometry_,
- 'MultiPolygon': WKT.encodeMultiPolygonGeometry_,
- 'GeometryCollection': WKT.encodeGeometryCollectionGeometry_
-};
+}
/**
@@ -245,8 +710,8 @@ WKT.GeometryEncoder_ = {
* @private
*/
WKT.prototype.parse_ = function(wkt) {
- const lexer = new WKT.Lexer(wkt);
- const parser = new WKT.Parser(lexer);
+ const lexer = new Lexer(wkt);
+ const parser = new Parser(lexer);
return parser.parse();
};
@@ -340,6 +805,58 @@ WKT.prototype.readGeometryFromText = function(text, opt_options) {
};
+/**
+ * @enum {function (new:ol.geom.Geometry, Array, ol.geom.GeometryLayout)}
+ */
+const GeometryConstructor = {
+ 'POINT': Point,
+ 'LINESTRING': LineString,
+ 'POLYGON': Polygon,
+ 'MULTIPOINT': MultiPoint,
+ 'MULTILINESTRING': MultiLineString,
+ 'MULTIPOLYGON': MultiPolygon
+};
+
+
+/**
+ * @enum {(function(): Array)}
+ */
+const GeometryParser = {
+ 'POINT': Parser.prototype.parsePointText_,
+ 'LINESTRING': Parser.prototype.parseLineStringText_,
+ 'POLYGON': Parser.prototype.parsePolygonText_,
+ 'MULTIPOINT': Parser.prototype.parseMultiPointText_,
+ 'MULTILINESTRING': Parser.prototype.parseMultiLineStringText_,
+ 'MULTIPOLYGON': Parser.prototype.parseMultiPolygonText_
+};
+
+
+/**
+ * @return {!ol.geom.Geometry} The geometry.
+ * @private
+ */
+Parser.prototype.parseGeometry_ = function() {
+ const token = this.token_;
+ if (this.match(TokenType.TEXT)) {
+ const geomType = token.value;
+ this.layout_ = this.parseGeometryLayout_();
+ if (geomType == GeometryType.GEOMETRY_COLLECTION.toUpperCase()) {
+ const geometries = this.parseGeometryCollectionText_();
+ return new GeometryCollection(geometries);
+ } else {
+ const parser = GeometryParser[geomType];
+ const ctor = GeometryConstructor[geomType];
+ if (!parser || !ctor) {
+ throw new Error('Invalid geometry type: ' + geomType);
+ }
+ const coordinates = parser.call(this);
+ return new ctor(coordinates, this.layout_);
+ }
+ }
+ throw new Error(this.formatErrorMessage_());
+};
+
+
/**
* Encode a feature as a WKT string.
*
@@ -408,538 +925,9 @@ WKT.prototype.writeGeometry;
* @inheritDoc
*/
WKT.prototype.writeGeometryText = function(geometry, opt_options) {
- return WKT.encode_(/** @type {ol.geom.Geometry} */ (
+ return encode(/** @type {ol.geom.Geometry} */ (
transformWithOptions(geometry, true, opt_options)));
};
-/**
- * @const
- * @enum {number}
- * @private
- */
-WKT.TokenType_ = {
- TEXT: 1,
- LEFT_PAREN: 2,
- RIGHT_PAREN: 3,
- NUMBER: 4,
- COMMA: 5,
- EOF: 6
-};
-
-
-/**
- * Class to tokenize a WKT string.
- * @param {string} wkt WKT string.
- * @constructor
- * @protected
- */
-WKT.Lexer = function(wkt) {
-
- /**
- * @type {string}
- */
- this.wkt = wkt;
-
- /**
- * @type {number}
- * @private
- */
- this.index_ = -1;
-};
-
-
-/**
- * @param {string} c Character.
- * @return {boolean} Whether the character is alphabetic.
- * @private
- */
-WKT.Lexer.prototype.isAlpha_ = function(c) {
- return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
-};
-
-
-/**
- * @param {string} c Character.
- * @param {boolean=} opt_decimal Whether the string number
- * contains a dot, i.e. is a decimal number.
- * @return {boolean} Whether the character is numeric.
- * @private
- */
-WKT.Lexer.prototype.isNumeric_ = function(c, opt_decimal) {
- const decimal = opt_decimal !== undefined ? opt_decimal : false;
- return c >= '0' && c <= '9' || c == '.' && !decimal;
-};
-
-
-/**
- * @param {string} c Character.
- * @return {boolean} Whether the character is whitespace.
- * @private
- */
-WKT.Lexer.prototype.isWhiteSpace_ = function(c) {
- return c == ' ' || c == '\t' || c == '\r' || c == '\n';
-};
-
-
-/**
- * @return {string} Next string character.
- * @private
- */
-WKT.Lexer.prototype.nextChar_ = function() {
- return this.wkt.charAt(++this.index_);
-};
-
-
-/**
- * Fetch and return the next token.
- * @return {!ol.WKTToken} Next string token.
- */
-WKT.Lexer.prototype.nextToken = function() {
- const c = this.nextChar_();
- const token = {position: this.index_, value: c};
-
- if (c == '(') {
- token.type = WKT.TokenType_.LEFT_PAREN;
- } else if (c == ',') {
- token.type = WKT.TokenType_.COMMA;
- } else if (c == ')') {
- token.type = WKT.TokenType_.RIGHT_PAREN;
- } else if (this.isNumeric_(c) || c == '-') {
- token.type = WKT.TokenType_.NUMBER;
- token.value = this.readNumber_();
- } else if (this.isAlpha_(c)) {
- token.type = WKT.TokenType_.TEXT;
- token.value = this.readText_();
- } else if (this.isWhiteSpace_(c)) {
- return this.nextToken();
- } else if (c === '') {
- token.type = WKT.TokenType_.EOF;
- } else {
- throw new Error('Unexpected character: ' + c);
- }
-
- return token;
-};
-
-
-/**
- * @return {number} Numeric token value.
- * @private
- */
-WKT.Lexer.prototype.readNumber_ = function() {
- let c;
- const index = this.index_;
- let decimal = false;
- let scientificNotation = false;
- do {
- if (c == '.') {
- decimal = true;
- } else if (c == 'e' || c == 'E') {
- scientificNotation = true;
- }
- c = this.nextChar_();
- } while (
- this.isNumeric_(c, decimal) ||
- // if we haven't detected a scientific number before, 'e' or 'E'
- // hint that we should continue to read
- !scientificNotation && (c == 'e' || c == 'E') ||
- // once we know that we have a scientific number, both '-' and '+'
- // are allowed
- scientificNotation && (c == '-' || c == '+')
- );
- return parseFloat(this.wkt.substring(index, this.index_--));
-};
-
-
-/**
- * @return {string} String token value.
- * @private
- */
-WKT.Lexer.prototype.readText_ = function() {
- let c;
- const index = this.index_;
- do {
- c = this.nextChar_();
- } while (this.isAlpha_(c));
- return this.wkt.substring(index, this.index_--).toUpperCase();
-};
-
-
-/**
- * Class to parse the tokens from the WKT string.
- * @param {ol.format.WKT.Lexer} lexer The lexer.
- * @constructor
- * @protected
- */
-WKT.Parser = function(lexer) {
-
- /**
- * @type {ol.format.WKT.Lexer}
- * @private
- */
- this.lexer_ = lexer;
-
- /**
- * @type {ol.WKTToken}
- * @private
- */
- this.token_;
-
- /**
- * @type {ol.geom.GeometryLayout}
- * @private
- */
- this.layout_ = GeometryLayout.XY;
-};
-
-
-/**
- * Fetch the next token form the lexer and replace the active token.
- * @private
- */
-WKT.Parser.prototype.consume_ = function() {
- this.token_ = this.lexer_.nextToken();
-};
-
-/**
- * Tests if the given type matches the type of the current token.
- * @param {ol.format.WKT.TokenType_} type Token type.
- * @return {boolean} Whether the token matches the given type.
- */
-WKT.Parser.prototype.isTokenType = function(type) {
- const isMatch = this.token_.type == type;
- return isMatch;
-};
-
-
-/**
- * If the given type matches the current token, consume it.
- * @param {ol.format.WKT.TokenType_} type Token type.
- * @return {boolean} Whether the token matches the given type.
- */
-WKT.Parser.prototype.match = function(type) {
- const isMatch = this.isTokenType(type);
- if (isMatch) {
- this.consume_();
- }
- return isMatch;
-};
-
-
-/**
- * Try to parse the tokens provided by the lexer.
- * @return {ol.geom.Geometry} The geometry.
- */
-WKT.Parser.prototype.parse = function() {
- this.consume_();
- const geometry = this.parseGeometry_();
- return geometry;
-};
-
-
-/**
- * Try to parse the dimensional info.
- * @return {ol.geom.GeometryLayout} The layout.
- * @private
- */
-WKT.Parser.prototype.parseGeometryLayout_ = function() {
- let layout = GeometryLayout.XY;
- const dimToken = this.token_;
- if (this.isTokenType(WKT.TokenType_.TEXT)) {
- const dimInfo = dimToken.value;
- if (dimInfo === WKT.Z) {
- layout = GeometryLayout.XYZ;
- } else if (dimInfo === WKT.M) {
- layout = GeometryLayout.XYM;
- } else if (dimInfo === WKT.ZM) {
- layout = GeometryLayout.XYZM;
- }
- if (layout !== GeometryLayout.XY) {
- this.consume_();
- }
- }
- return layout;
-};
-
-
-/**
- * @return {!ol.geom.Geometry} The geometry.
- * @private
- */
-WKT.Parser.prototype.parseGeometry_ = function() {
- const token = this.token_;
- if (this.match(WKT.TokenType_.TEXT)) {
- const geomType = token.value;
- this.layout_ = this.parseGeometryLayout_();
- if (geomType == GeometryType.GEOMETRY_COLLECTION.toUpperCase()) {
- const geometries = this.parseGeometryCollectionText_();
- return new GeometryCollection(geometries);
- } else {
- const parser = WKT.Parser.GeometryParser_[geomType];
- const ctor = WKT.Parser.GeometryConstructor_[geomType];
- if (!parser || !ctor) {
- throw new Error('Invalid geometry type: ' + geomType);
- }
- const coordinates = parser.call(this);
- return new ctor(coordinates, this.layout_);
- }
- }
- throw new Error(this.formatErrorMessage_());
-};
-
-
-/**
- * @return {!Array.} A collection of geometries.
- * @private
- */
-WKT.Parser.prototype.parseGeometryCollectionText_ = function() {
- if (this.match(WKT.TokenType_.LEFT_PAREN)) {
- const geometries = [];
- do {
- geometries.push(this.parseGeometry_());
- } while (this.match(WKT.TokenType_.COMMA));
- if (this.match(WKT.TokenType_.RIGHT_PAREN)) {
- return geometries;
- }
- } else if (this.isEmptyGeometry_()) {
- return [];
- }
- throw new Error(this.formatErrorMessage_());
-};
-
-
-/**
- * @return {Array.} All values in a point.
- * @private
- */
-WKT.Parser.prototype.parsePointText_ = function() {
- if (this.match(WKT.TokenType_.LEFT_PAREN)) {
- const coordinates = this.parsePoint_();
- if (this.match(WKT.TokenType_.RIGHT_PAREN)) {
- return coordinates;
- }
- } else if (this.isEmptyGeometry_()) {
- return null;
- }
- throw new Error(this.formatErrorMessage_());
-};
-
-
-/**
- * @return {!Array.>} All points in a linestring.
- * @private
- */
-WKT.Parser.prototype.parseLineStringText_ = function() {
- if (this.match(WKT.TokenType_.LEFT_PAREN)) {
- const coordinates = this.parsePointList_();
- if (this.match(WKT.TokenType_.RIGHT_PAREN)) {
- return coordinates;
- }
- } else if (this.isEmptyGeometry_()) {
- return [];
- }
- throw new Error(this.formatErrorMessage_());
-};
-
-
-/**
- * @return {!Array.>} All points in a polygon.
- * @private
- */
-WKT.Parser.prototype.parsePolygonText_ = function() {
- if (this.match(WKT.TokenType_.LEFT_PAREN)) {
- const coordinates = this.parseLineStringTextList_();
- if (this.match(WKT.TokenType_.RIGHT_PAREN)) {
- return coordinates;
- }
- } else if (this.isEmptyGeometry_()) {
- return [];
- }
- throw new Error(this.formatErrorMessage_());
-};
-
-
-/**
- * @return {!Array.>} All points in a multipoint.
- * @private
- */
-WKT.Parser.prototype.parseMultiPointText_ = function() {
- if (this.match(WKT.TokenType_.LEFT_PAREN)) {
- let coordinates;
- if (this.token_.type == WKT.TokenType_.LEFT_PAREN) {
- coordinates = this.parsePointTextList_();
- } else {
- coordinates = this.parsePointList_();
- }
- if (this.match(WKT.TokenType_.RIGHT_PAREN)) {
- return coordinates;
- }
- } else if (this.isEmptyGeometry_()) {
- return [];
- }
- throw new Error(this.formatErrorMessage_());
-};
-
-
-/**
- * @return {!Array.>} All linestring points
- * in a multilinestring.
- * @private
- */
-WKT.Parser.prototype.parseMultiLineStringText_ = function() {
- if (this.match(WKT.TokenType_.LEFT_PAREN)) {
- const coordinates = this.parseLineStringTextList_();
- if (this.match(WKT.TokenType_.RIGHT_PAREN)) {
- return coordinates;
- }
- } else if (this.isEmptyGeometry_()) {
- return [];
- }
- throw new Error(this.formatErrorMessage_());
-};
-
-
-/**
- * @return {!Array.>} All polygon points in a multipolygon.
- * @private
- */
-WKT.Parser.prototype.parseMultiPolygonText_ = function() {
- if (this.match(WKT.TokenType_.LEFT_PAREN)) {
- const coordinates = this.parsePolygonTextList_();
- if (this.match(WKT.TokenType_.RIGHT_PAREN)) {
- return coordinates;
- }
- } else if (this.isEmptyGeometry_()) {
- return [];
- }
- throw new Error(this.formatErrorMessage_());
-};
-
-
-/**
- * @return {!Array.} A point.
- * @private
- */
-WKT.Parser.prototype.parsePoint_ = function() {
- const coordinates = [];
- const dimensions = this.layout_.length;
- for (let i = 0; i < dimensions; ++i) {
- const token = this.token_;
- if (this.match(WKT.TokenType_.NUMBER)) {
- coordinates.push(token.value);
- } else {
- break;
- }
- }
- if (coordinates.length == dimensions) {
- return coordinates;
- }
- throw new Error(this.formatErrorMessage_());
-};
-
-
-/**
- * @return {!Array.>} An array of points.
- * @private
- */
-WKT.Parser.prototype.parsePointList_ = function() {
- const coordinates = [this.parsePoint_()];
- while (this.match(WKT.TokenType_.COMMA)) {
- coordinates.push(this.parsePoint_());
- }
- return coordinates;
-};
-
-
-/**
- * @return {!Array.>} An array of points.
- * @private
- */
-WKT.Parser.prototype.parsePointTextList_ = function() {
- const coordinates = [this.parsePointText_()];
- while (this.match(WKT.TokenType_.COMMA)) {
- coordinates.push(this.parsePointText_());
- }
- return coordinates;
-};
-
-
-/**
- * @return {!Array.>} An array of points.
- * @private
- */
-WKT.Parser.prototype.parseLineStringTextList_ = function() {
- const coordinates = [this.parseLineStringText_()];
- while (this.match(WKT.TokenType_.COMMA)) {
- coordinates.push(this.parseLineStringText_());
- }
- return coordinates;
-};
-
-
-/**
- * @return {!Array.>} An array of points.
- * @private
- */
-WKT.Parser.prototype.parsePolygonTextList_ = function() {
- const coordinates = [this.parsePolygonText_()];
- while (this.match(WKT.TokenType_.COMMA)) {
- coordinates.push(this.parsePolygonText_());
- }
- return coordinates;
-};
-
-
-/**
- * @return {boolean} Whether the token implies an empty geometry.
- * @private
- */
-WKT.Parser.prototype.isEmptyGeometry_ = function() {
- const isEmpty = this.isTokenType(WKT.TokenType_.TEXT) &&
- this.token_.value == WKT.EMPTY;
- if (isEmpty) {
- this.consume_();
- }
- return isEmpty;
-};
-
-
-/**
- * Create an error message for an unexpected token error.
- * @return {string} Error message.
- * @private
- */
-WKT.Parser.prototype.formatErrorMessage_ = function() {
- return 'Unexpected `' + this.token_.value + '` at position ' +
- this.token_.position + ' in `' + this.lexer_.wkt + '`';
-};
-
-
-/**
- * @enum {function (new:ol.geom.Geometry, Array, ol.geom.GeometryLayout)}
- * @private
- */
-WKT.Parser.GeometryConstructor_ = {
- 'POINT': Point,
- 'LINESTRING': LineString,
- 'POLYGON': Polygon,
- 'MULTIPOINT': MultiPoint,
- 'MULTILINESTRING': MultiLineString,
- 'MULTIPOLYGON': MultiPolygon
-};
-
-
-/**
- * @enum {(function(): Array)}
- * @private
- */
-WKT.Parser.GeometryParser_ = {
- 'POINT': WKT.Parser.prototype.parsePointText_,
- 'LINESTRING': WKT.Parser.prototype.parseLineStringText_,
- 'POLYGON': WKT.Parser.prototype.parsePolygonText_,
- 'MULTIPOINT': WKT.Parser.prototype.parseMultiPointText_,
- 'MULTILINESTRING': WKT.Parser.prototype.parseMultiLineStringText_,
- 'MULTIPOLYGON': WKT.Parser.prototype.parseMultiPolygonText_
-};
export default WKT;