Fix strict null checking for format/WKT

NOTE: `EMPTY` may only occur immediately after geometry type
This commit is contained in:
Simon Seyock
2021-02-03 22:40:14 +01:00
parent 8facb252f1
commit bd489fec4c
3 changed files with 70 additions and 84 deletions
+5 -5
View File
@@ -67,15 +67,15 @@ class FeatureFormat {
constructor() { constructor() {
/** /**
* @protected * @protected
* @type {import("../proj/Projection.js").default} * @type {import("../proj/Projection.js").default|undefined}
*/ */
this.dataProjection = null; this.dataProjection = undefined;
/** /**
* @protected * @protected
* @type {import("../proj/Projection.js").default} * @type {import("../proj/Projection.js").default|undefined}
*/ */
this.defaultFeatureProjection = null; this.defaultFeatureProjection = undefined;
} }
/** /**
@@ -175,7 +175,7 @@ class FeatureFormat {
* *
* @abstract * @abstract
* @param {Document|Element|Object|string} source Source. * @param {Document|Element|Object|string} source Source.
* @return {import("../proj/Projection.js").default} Projection. * @return {import("../proj/Projection.js").default|undefined} Projection.
*/ */
readProjection(source) { readProjection(source) {
return abstract(); return abstract();
+2 -2
View File
@@ -107,7 +107,7 @@ class TextFeature extends FeatureFormat {
* Read the projection from the source. * Read the projection from the source.
* *
* @param {Document|Element|Object|string} source Source. * @param {Document|Element|Object|string} source Source.
* @return {import("../proj/Projection.js").default} Projection. * @return {import("../proj/Projection.js").default|undefined} Projection.
* @api * @api
*/ */
readProjection(source) { readProjection(source) {
@@ -117,7 +117,7 @@ class TextFeature extends FeatureFormat {
/** /**
* @param {string} text Text. * @param {string} text Text.
* @protected * @protected
* @return {import("../proj/Projection.js").default} Projection. * @return {import("../proj/Projection.js").default|undefined} Projection.
*/ */
readProjectionFromText(text) { readProjectionFromText(text) {
return this.dataProjection; return this.dataProjection;
+63 -77
View File
@@ -69,6 +69,7 @@ const ZM = 'ZM';
* @enum {number} * @enum {number}
*/ */
const TokenType = { const TokenType = {
START: 0,
TEXT: 1, TEXT: 1,
LEFT_PAREN: 2, LEFT_PAREN: 2,
RIGHT_PAREN: 3, RIGHT_PAREN: 3,
@@ -146,7 +147,7 @@ class Lexer {
/** /**
* Fetch and return the next token. * Fetch and return the next token.
* @return {!Token} Next string token. * @return {Token} Next string token.
*/ */
nextToken() { nextToken() {
const c = this.nextChar_(); const c = this.nextChar_();
@@ -238,7 +239,10 @@ class Parser {
* @type {Token} * @type {Token}
* @private * @private
*/ */
this.token_; this.token_ = {
position: 0,
type: TokenType.START,
};
/** /**
* @type {import("../geom/GeometryLayout.js").default} * @type {import("../geom/GeometryLayout.js").default}
@@ -261,8 +265,7 @@ class Parser {
* @return {boolean} Whether the token matches the given type. * @return {boolean} Whether the token matches the given type.
*/ */
isTokenType(type) { isTokenType(type) {
const isMatch = this.token_.type == type; return this.token_.type == type;
return isMatch;
} }
/** /**
@@ -284,8 +287,7 @@ class Parser {
*/ */
parse() { parse() {
this.consume_(); this.consume_();
const geometry = this.parseGeometry_(); return this.parseGeometry_();
return geometry;
} }
/** /**
@@ -313,7 +315,7 @@ class Parser {
} }
/** /**
* @return {!Array<import("../geom/Geometry.js").default>} A collection of geometries. * @return {Array<import("../geom/Geometry.js").default>} A collection of geometries.
* @private * @private
*/ */
parseGeometryCollectionText_() { parseGeometryCollectionText_() {
@@ -325,8 +327,6 @@ class Parser {
if (this.match(TokenType.RIGHT_PAREN)) { if (this.match(TokenType.RIGHT_PAREN)) {
return geometries; return geometries;
} }
} else if (this.isEmptyGeometry_()) {
return [];
} }
throw new Error(this.formatErrorMessage_()); throw new Error(this.formatErrorMessage_());
} }
@@ -341,14 +341,12 @@ class Parser {
if (this.match(TokenType.RIGHT_PAREN)) { if (this.match(TokenType.RIGHT_PAREN)) {
return coordinates; return coordinates;
} }
} else if (this.isEmptyGeometry_()) {
return null;
} }
throw new Error(this.formatErrorMessage_()); throw new Error(this.formatErrorMessage_());
} }
/** /**
* @return {!Array<!Array<number>>} All points in a linestring. * @return {Array<Array<number>>} All points in a linestring.
* @private * @private
*/ */
parseLineStringText_() { parseLineStringText_() {
@@ -357,14 +355,12 @@ class Parser {
if (this.match(TokenType.RIGHT_PAREN)) { if (this.match(TokenType.RIGHT_PAREN)) {
return coordinates; return coordinates;
} }
} else if (this.isEmptyGeometry_()) {
return [];
} }
throw new Error(this.formatErrorMessage_()); throw new Error(this.formatErrorMessage_());
} }
/** /**
* @return {!Array<!Array<!Array<number>>>} All points in a polygon. * @return {Array<Array<Array<number>>>} All points in a polygon.
* @private * @private
*/ */
parsePolygonText_() { parsePolygonText_() {
@@ -373,14 +369,12 @@ class Parser {
if (this.match(TokenType.RIGHT_PAREN)) { if (this.match(TokenType.RIGHT_PAREN)) {
return coordinates; return coordinates;
} }
} else if (this.isEmptyGeometry_()) {
return [];
} }
throw new Error(this.formatErrorMessage_()); throw new Error(this.formatErrorMessage_());
} }
/** /**
* @return {!Array<!Array<number>>} All points in a multipoint. * @return {Array<Array<number>>} All points in a multipoint.
* @private * @private
*/ */
parseMultiPointText_() { parseMultiPointText_() {
@@ -394,14 +388,12 @@ class Parser {
if (this.match(TokenType.RIGHT_PAREN)) { if (this.match(TokenType.RIGHT_PAREN)) {
return coordinates; return coordinates;
} }
} else if (this.isEmptyGeometry_()) {
return [];
} }
throw new Error(this.formatErrorMessage_()); throw new Error(this.formatErrorMessage_());
} }
/** /**
* @return {!Array<!Array<!Array<number>>>} All linestring points * @return {Array<Array<Array<number>>>} All linestring points
* in a multilinestring. * in a multilinestring.
* @private * @private
*/ */
@@ -411,14 +403,12 @@ class Parser {
if (this.match(TokenType.RIGHT_PAREN)) { if (this.match(TokenType.RIGHT_PAREN)) {
return coordinates; return coordinates;
} }
} else if (this.isEmptyGeometry_()) {
return [];
} }
throw new Error(this.formatErrorMessage_()); throw new Error(this.formatErrorMessage_());
} }
/** /**
* @return {!Array<!Array<!Array<!Array<number>>>>} All polygon points in a multipolygon. * @return {Array<Array<Array<Array<number>>>>} All polygon points in a multipolygon.
* @private * @private
*/ */
parseMultiPolygonText_() { parseMultiPolygonText_() {
@@ -427,14 +417,12 @@ class Parser {
if (this.match(TokenType.RIGHT_PAREN)) { if (this.match(TokenType.RIGHT_PAREN)) {
return coordinates; return coordinates;
} }
} else if (this.isEmptyGeometry_()) {
return [];
} }
throw new Error(this.formatErrorMessage_()); throw new Error(this.formatErrorMessage_());
} }
/** /**
* @return {!Array<number>} A point. * @return {Array<number>} A point.
* @private * @private
*/ */
parsePoint_() { parsePoint_() {
@@ -455,7 +443,7 @@ class Parser {
} }
/** /**
* @return {!Array<!Array<number>>} An array of points. * @return {Array<Array<number>>} An array of points.
* @private * @private
*/ */
parsePointList_() { parsePointList_() {
@@ -467,7 +455,7 @@ class Parser {
} }
/** /**
* @return {!Array<!Array<number>>} An array of points. * @return {Array<Array<number>>} An array of points.
* @private * @private
*/ */
parsePointTextList_() { parsePointTextList_() {
@@ -479,7 +467,7 @@ class Parser {
} }
/** /**
* @return {!Array<!Array<!Array<number>>>} An array of points. * @return {Array<Array<Array<number>>>} An array of points.
* @private * @private
*/ */
parseLineStringTextList_() { parseLineStringTextList_() {
@@ -491,7 +479,7 @@ class Parser {
} }
/** /**
* @return {!Array<!Array<!Array<!Array<number>>>>} An array of points. * @return {Array<Array<Array<Array<number>>>>} An array of points.
* @private * @private
*/ */
parsePolygonTextList_() { parsePolygonTextList_() {
@@ -533,15 +521,19 @@ class Parser {
} }
/** /**
* @return {!import("../geom/Geometry.js").default} The geometry. * @return {import("../geom/Geometry.js").default} The geometry.
* @private * @private
*/ */
parseGeometry_() { parseGeometry_() {
const token = this.token_; const token = this.token_;
if (this.match(TokenType.TEXT)) { if (this.match(TokenType.TEXT)) {
const geomType = token.value; const geomType = /** @type {string} */ (token.value);
this.layout_ = this.parseGeometryLayout_(); this.layout_ = this.parseGeometryLayout_();
const isEmpty = this.isEmptyGeometry_();
if (geomType == 'GEOMETRYCOLLECTION') { if (geomType == 'GEOMETRYCOLLECTION') {
if (isEmpty) {
return new GeometryCollection([]);
}
const geometries = this.parseGeometryCollectionText_(); const geometries = this.parseGeometryCollectionText_();
return new GeometryCollection(geometries); return new GeometryCollection(geometries);
} else { } else {
@@ -551,43 +543,44 @@ class Parser {
} }
let coordinates; let coordinates;
switch (geomType) {
case 'POINT': {
coordinates = this.parsePointText_();
break;
}
case 'LINESTRING': {
coordinates = this.parseLineStringText_();
break;
}
case 'POLYGON': {
coordinates = this.parsePolygonText_();
break;
}
case 'MULTIPOINT': {
coordinates = this.parseMultiPointText_();
break;
}
case 'MULTILINESTRING': {
coordinates = this.parseMultiLineStringText_();
break;
}
case 'MULTIPOLYGON': {
coordinates = this.parseMultiPolygonText_();
break;
}
default: {
throw new Error('Invalid geometry type: ' + geomType);
}
}
if (!coordinates) { if (isEmpty) {
if (ctor === GeometryConstructor['POINT']) { if (geomType == 'POINT') {
coordinates = [NaN, NaN]; coordinates = [NaN, NaN];
} else { } else {
coordinates = []; coordinates = [];
} }
} else {
switch (geomType) {
case 'POINT': {
coordinates = this.parsePointText_();
break;
}
case 'LINESTRING': {
coordinates = this.parseLineStringText_();
break;
}
case 'POLYGON': {
coordinates = this.parsePolygonText_();
break;
}
case 'MULTIPOINT': {
coordinates = this.parseMultiPointText_();
break;
}
case 'MULTILINESTRING': {
coordinates = this.parseMultiLineStringText_();
break;
}
case 'MULTIPOLYGON': {
coordinates = this.parseMultiPolygonText_();
break;
}
default:
break;
}
} }
return new ctor(coordinates, this.layout_); return new ctor(coordinates, this.layout_);
} }
} }
@@ -623,7 +616,7 @@ class WKT extends TextFeature {
/** /**
* Parse a WKT string. * Parse a WKT string.
* @param {string} wkt WKT string. * @param {string} wkt WKT string.
* @return {import("../geom/Geometry.js").default|undefined} * @return {import("../geom/Geometry.js").default}
* The geometry created. * The geometry created.
* @private * @private
*/ */
@@ -641,12 +634,9 @@ class WKT extends TextFeature {
*/ */
readFeatureFromText(text, opt_options) { readFeatureFromText(text, opt_options) {
const geom = this.readGeometryFromText(text, opt_options); const geom = this.readGeometryFromText(text, opt_options);
if (geom) { const feature = new Feature();
const feature = new Feature(); feature.setGeometry(geom);
feature.setGeometry(geom); return feature;
return feature;
}
return null;
} }
/** /**
@@ -683,11 +673,7 @@ class WKT extends TextFeature {
*/ */
readGeometryFromText(text, opt_options) { readGeometryFromText(text, opt_options) {
const geometry = this.parse_(text); const geometry = this.parse_(text);
if (geometry) { return transformGeometryWithOptions(geometry, false, opt_options);
return transformGeometryWithOptions(geometry, false, opt_options);
} else {
return null;
}
} }
/** /**
@@ -855,7 +841,7 @@ const GeometryEncoder = {
/** /**
* Encode a geometry as WKT. * Encode a geometry as WKT.
* @param {!import("../geom/Geometry.js").default} geom The geometry to encode. * @param {import("../geom/Geometry.js").default} geom The geometry to encode.
* @return {string} WKT string for the geometry. * @return {string} WKT string for the geometry.
*/ */
function encode(geom) { function encode(geom) {