Merge pull request #813 from Turbo87/encoded-polyline
Format: Added EncodedPolyline class. Merging this as a first step towards support for alternative geometry encodings. See #813 for TBDs.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -306,6 +306,7 @@
|
|||||||
"OpenLayers/Format/GML/v2.js",
|
"OpenLayers/Format/GML/v2.js",
|
||||||
"OpenLayers/Format/GML/v3.js",
|
"OpenLayers/Format/GML/v3.js",
|
||||||
"OpenLayers/Format/Atom.js",
|
"OpenLayers/Format/Atom.js",
|
||||||
|
"OpenLayers/Format/EncodedPolyline.js",
|
||||||
"OpenLayers/Format/KML.js",
|
"OpenLayers/Format/KML.js",
|
||||||
"OpenLayers/Format/GeoRSS.js",
|
"OpenLayers/Format/GeoRSS.js",
|
||||||
"OpenLayers/Format/WFS.js",
|
"OpenLayers/Format/WFS.js",
|
||||||
|
|||||||
@@ -0,0 +1,254 @@
|
|||||||
|
/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
|
||||||
|
* full list of contributors). Published under the 2-clause BSD license.
|
||||||
|
* See license.txt in the OpenLayers distribution or repository for the
|
||||||
|
* full text of the license. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @requires OpenLayers/Format.js
|
||||||
|
* @requires OpenLayers/Feature/Vector.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class: OpenLayers.Format.EncodedPolyline
|
||||||
|
* Class for reading and writing encoded polylines. Create a new instance
|
||||||
|
* with the <OpenLayers.Format.EncodedPolyline> constructor.
|
||||||
|
*
|
||||||
|
* Inherits from:
|
||||||
|
* - <OpenLayers.Format>
|
||||||
|
*/
|
||||||
|
OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIProperty: geometryType
|
||||||
|
* {String} Geometry type to output. One of: linestring (default),
|
||||||
|
* linearring, point, multipoint or polygon. If the geometryType is
|
||||||
|
* point, only the first point of the string is returned.
|
||||||
|
*/
|
||||||
|
geometryType: "linestring",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor: OpenLayers.Format.EncodedPolyline
|
||||||
|
* Create a new parser for encoded polylines
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* options - {Object} An optional object whose properties will be set on
|
||||||
|
* this instance
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {<OpenLayers.Format.EncodedPolyline>} A new encoded polylines parser.
|
||||||
|
*/
|
||||||
|
initialize: function(options) {
|
||||||
|
OpenLayers.Format.prototype.initialize.apply(this, [options]);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: read
|
||||||
|
* Deserialize an encoded polyline string and return a vector feature.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* encoded - {String} An encoded polyline string
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {<OpenLayers.Feature.Vector>} A vector feature with a linestring.
|
||||||
|
*/
|
||||||
|
read: function(encoded) {
|
||||||
|
var geomType;
|
||||||
|
if (this.geometryType == "linestring")
|
||||||
|
geomType = OpenLayers.Geometry.LineString;
|
||||||
|
else if (this.geometryType == "linearring")
|
||||||
|
geomType = OpenLayers.Geometry.LinearRing;
|
||||||
|
else if (this.geometryType == "multipoint")
|
||||||
|
geomType = OpenLayers.Geometry.MultiPoint;
|
||||||
|
else if (this.geometryType != "point" && this.geometryType != "polygon")
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var points = this.decode(encoded, 2);
|
||||||
|
var pointGeometries = new Array();
|
||||||
|
for (i in points) {
|
||||||
|
var point = points[i];
|
||||||
|
pointGeometries.push(
|
||||||
|
new OpenLayers.Geometry.Point(point[1] * 1e-5, point[0] * 1e-5)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.geometryType == "point")
|
||||||
|
return new OpenLayers.Feature.Vector(
|
||||||
|
pointGeometries[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.geometryType == "polygon")
|
||||||
|
return new OpenLayers.Feature.Vector(
|
||||||
|
new OpenLayers.Geometry.Polygon([
|
||||||
|
new OpenLayers.Geometry.LinearRing(pointGeometries)
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
return new OpenLayers.Feature.Vector(
|
||||||
|
new geomType(pointGeometries)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: decode
|
||||||
|
* Deserialize an encoded string and return an array of n-dimensional
|
||||||
|
* points.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* encoded - {String} An encoded string
|
||||||
|
* dims - {int} The dimension of the points that are returned
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {Array(Array(int))} An array containing n-dimensional arrays of
|
||||||
|
* coordinates.
|
||||||
|
*/
|
||||||
|
decode: function(encoded, dims) {
|
||||||
|
var points = new Array();
|
||||||
|
var point = new Array(dims);
|
||||||
|
|
||||||
|
// Reset the point array
|
||||||
|
for (var i = 0; i < point.length; ++i)
|
||||||
|
point[i] = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < encoded.length;) {
|
||||||
|
for (var dim = 0; dim < dims; ++dim) {
|
||||||
|
var result = 0;
|
||||||
|
var shift = 0;
|
||||||
|
|
||||||
|
var b;
|
||||||
|
do {
|
||||||
|
b = encoded.charCodeAt(i++) - 63;
|
||||||
|
result |= (b & 0x1f) << shift;
|
||||||
|
shift += 5;
|
||||||
|
} while (b >= 0x20);
|
||||||
|
|
||||||
|
point[dim] += ((result & 1) ? ~(result >> 1) : (result >> 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
points.push(point.slice(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: write
|
||||||
|
* Serialize a feature or array of features into a WKT string.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* features - {<OpenLayers.Feature.Vector>|Array} A feature or array of
|
||||||
|
* features
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {String} The WKT string representation of the input geometries
|
||||||
|
*/
|
||||||
|
write: function(features) {
|
||||||
|
var feature;
|
||||||
|
if (features.constructor == Array)
|
||||||
|
feature = features[0];
|
||||||
|
else
|
||||||
|
feature = features;
|
||||||
|
|
||||||
|
var geometry = feature.geometry;
|
||||||
|
var type = geometry.CLASS_NAME.split('.')[2].toLowerCase();
|
||||||
|
|
||||||
|
var pointGeometries;
|
||||||
|
if (type == "point")
|
||||||
|
pointGeometries = new Array(geometry);
|
||||||
|
else if (type == "linestring" ||
|
||||||
|
type == "linearring" ||
|
||||||
|
type == "multipoint")
|
||||||
|
pointGeometries = geometry.components;
|
||||||
|
else if (type == "polygon")
|
||||||
|
pointGeometries = geometry.components[0].components;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var points = new Array();
|
||||||
|
for (var i in pointGeometries) {
|
||||||
|
var pointGeometry = pointGeometries[i];
|
||||||
|
var point = [Math.round(pointGeometry.y * 1e5),
|
||||||
|
Math.round(pointGeometry.x * 1e5)];
|
||||||
|
points.push(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = this.encode(points, 2);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: encode
|
||||||
|
* Serialize an array of n-dimensional points and return an encoded string
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* points - {Array(Array(int))} An array containing n-dimensional
|
||||||
|
* arrays of coordinates
|
||||||
|
* dims - {int} The dimension of the points that should be read
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {String} An encoded string
|
||||||
|
*/
|
||||||
|
encode: function (points, dims) {
|
||||||
|
var encoded_points = "";
|
||||||
|
|
||||||
|
var lastPoint = new Array(dims);
|
||||||
|
for (var i = 0; i < lastPoint.length; ++i)
|
||||||
|
lastPoint[i] = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < points.length; i++) {
|
||||||
|
var point = points[i];
|
||||||
|
|
||||||
|
for (var dim = 0; dim < lastPoint.length; ++dim) {
|
||||||
|
var delta = point[dim] - lastPoint[dim];
|
||||||
|
encoded_points += this.encodeSignedNumber(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPoint = point;
|
||||||
|
}
|
||||||
|
return encoded_points;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: encodeSignedNumber
|
||||||
|
* Encode one single signed integer and return an encoded string
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* num - {int} A signed integer that should be encoded
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {String} An encoded string
|
||||||
|
*/
|
||||||
|
encodeSignedNumber: function (num) {
|
||||||
|
var sgn_num = num << 1;
|
||||||
|
if (num < 0)
|
||||||
|
sgn_num = ~(sgn_num);
|
||||||
|
|
||||||
|
return this.encodeNumber(sgn_num);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: encodeNumber
|
||||||
|
* Encode one single unsigned integer and return an encoded string
|
||||||
|
*
|
||||||
|
* encodeSignedNumber should be used instead of using this method directly!
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* num - {int} An unsigned integer that should be encoded
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {String} An encoded string
|
||||||
|
*/
|
||||||
|
encodeNumber: function (num) {
|
||||||
|
var encodeString = "";
|
||||||
|
var value;
|
||||||
|
while (num >= 0x20) {
|
||||||
|
value = (0x20 | (num & 0x1f)) + 63;
|
||||||
|
encodeString += (String.fromCharCode(value));
|
||||||
|
num >>= 5;
|
||||||
|
}
|
||||||
|
value = num + 63;
|
||||||
|
encodeString += (String.fromCharCode(value));
|
||||||
|
return encodeString;
|
||||||
|
},
|
||||||
|
|
||||||
|
CLASS_NAME: "OpenLayers.Format.EncodedPolyline"
|
||||||
|
});
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="../OLLoader.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
var basePoints = new Array(
|
||||||
|
new Array(3850000, -12020000),
|
||||||
|
new Array(4070000, -12095000),
|
||||||
|
new Array(4325200, -12645300)
|
||||||
|
);
|
||||||
|
|
||||||
|
var points = [
|
||||||
|
new OpenLayers.Geometry.Point(basePoints[0][1] * 1e-5,
|
||||||
|
basePoints[0][0] * 1e-5),
|
||||||
|
new OpenLayers.Geometry.Point(basePoints[1][1] * 1e-5,
|
||||||
|
basePoints[1][0] * 1e-5),
|
||||||
|
new OpenLayers.Geometry.Point(basePoints[2][1] * 1e-5,
|
||||||
|
basePoints[2][0] * 1e-5)
|
||||||
|
];
|
||||||
|
|
||||||
|
var singlePoint = new OpenLayers.Feature.Vector(points[0]);
|
||||||
|
|
||||||
|
var linestring = new OpenLayers.Feature.Vector(
|
||||||
|
new OpenLayers.Geometry.LineString(points)
|
||||||
|
);
|
||||||
|
|
||||||
|
var multipoint = new OpenLayers.Feature.Vector(
|
||||||
|
new OpenLayers.Geometry.MultiPoint(points)
|
||||||
|
);
|
||||||
|
|
||||||
|
var linearring = new OpenLayers.Feature.Vector(
|
||||||
|
new OpenLayers.Geometry.LinearRing(points)
|
||||||
|
);
|
||||||
|
|
||||||
|
var polygon = new OpenLayers.Feature.Vector(
|
||||||
|
new OpenLayers.Geometry.Polygon([
|
||||||
|
new OpenLayers.Geometry.LinearRing(points)
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
var encoded = "_p~iF~ps|U_ulLnnqC_mqNvxq`@";
|
||||||
|
|
||||||
|
function test_Format_EncodedPolyline_constructor(t) {
|
||||||
|
t.plan(4);
|
||||||
|
|
||||||
|
var options = {'foo': 'bar'};
|
||||||
|
var format = new OpenLayers.Format.EncodedPolyline(options);
|
||||||
|
t.ok(format instanceof OpenLayers.Format.EncodedPolyline,
|
||||||
|
"new OpenLayers.Format.EncodedPolyline returns object" );
|
||||||
|
t.eq(format.foo, "bar", "constructor sets options correctly");
|
||||||
|
t.eq(typeof format.read, "function", "format has a read function");
|
||||||
|
t.eq(typeof format.write, "function", "format has a write function");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_Format_EncodedPolyline_read(t) {
|
||||||
|
t.plan(5);
|
||||||
|
|
||||||
|
var format = new OpenLayers.Format.EncodedPolyline();
|
||||||
|
|
||||||
|
t.ok(linestring.geometry.equals(format.read(encoded).geometry),
|
||||||
|
"format correctly reads encoded polyline");
|
||||||
|
|
||||||
|
format = new OpenLayers.Format.EncodedPolyline({
|
||||||
|
geometryType: "multipoint"
|
||||||
|
});
|
||||||
|
t.ok(multipoint.geometry.equals(format.read(encoded).geometry),
|
||||||
|
"format correctly reads encoded multipoint");
|
||||||
|
|
||||||
|
format.geometryType = "linearring";
|
||||||
|
t.ok(linearring.geometry.equals(format.read(encoded).geometry),
|
||||||
|
"format correctly reads encoded linearring");
|
||||||
|
|
||||||
|
format.geometryType = "polygon";
|
||||||
|
t.ok(polygon.geometry.equals(format.read(encoded).geometry),
|
||||||
|
"format correctly reads encoded polygon");
|
||||||
|
|
||||||
|
format.geometryType = "point";
|
||||||
|
t.ok(points[0].equals(format.read(encoded).geometry),
|
||||||
|
"format correctly reads encoded point");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_Format_EncodedPolyline_decode(t) {
|
||||||
|
t.plan(6);
|
||||||
|
|
||||||
|
var format = new OpenLayers.Format.EncodedPolyline();
|
||||||
|
|
||||||
|
var decodedPoints = format.decode(encoded, 2);
|
||||||
|
for (i in decodedPoints) {
|
||||||
|
var point = basePoints[i];
|
||||||
|
var decodedPoint = decodedPoints[i];
|
||||||
|
t.eq(point[0], decodedPoint[0]);
|
||||||
|
t.eq(point[1], decodedPoint[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_Format_EncodedPolyline_write(t) {
|
||||||
|
t.plan(5);
|
||||||
|
|
||||||
|
var format = new OpenLayers.Format.EncodedPolyline();
|
||||||
|
|
||||||
|
t.eq(format.write(linestring), encoded,
|
||||||
|
"format correctly writes encoded polyline");
|
||||||
|
|
||||||
|
t.eq(format.write(multipoint), encoded,
|
||||||
|
"format correctly writes encoded multipoint");
|
||||||
|
|
||||||
|
// Different output than encoded,
|
||||||
|
// because polygon closing point is included
|
||||||
|
t.eq(format.write(linearring),
|
||||||
|
"_p~iF~ps|U_ulLnnqC_mqNvxq`@~b_\\ghde@",
|
||||||
|
"format correctly writes encoded linearring");
|
||||||
|
|
||||||
|
t.eq(format.write(polygon),
|
||||||
|
"_p~iF~ps|U_ulLnnqC_mqNvxq`@~b_\\ghde@",
|
||||||
|
"format correctly writes encoded polygon");
|
||||||
|
|
||||||
|
t.eq(format.write(singlePoint), "_p~iF~ps|U",
|
||||||
|
"format correctly writes encoded point");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_Format_EncodedPolyline_encode(t) {
|
||||||
|
t.plan(1);
|
||||||
|
|
||||||
|
var format = new OpenLayers.Format.EncodedPolyline();
|
||||||
|
|
||||||
|
t.eq(format.encode(basePoints, 2), encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -66,6 +66,7 @@
|
|||||||
<li>Format/XML/VersionedOGC.html</li>
|
<li>Format/XML/VersionedOGC.html</li>
|
||||||
<li>Format/ArcXML/Features.html</li>
|
<li>Format/ArcXML/Features.html</li>
|
||||||
<li>Format/CQL.html</li>
|
<li>Format/CQL.html</li>
|
||||||
|
<li>Format/EncodedPolyline.html</li>
|
||||||
<li>Format/GeoJSON.html</li>
|
<li>Format/GeoJSON.html</li>
|
||||||
<li>Format/GeoRSS.html</li>
|
<li>Format/GeoRSS.html</li>
|
||||||
<li>Format/GML.html</li>
|
<li>Format/GML.html</li>
|
||||||
|
|||||||
Reference in New Issue
Block a user