read/write for Well-Known Text representation of vector geometries - support for simple features: Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon, and GeometryCollection - examples/wkt.html for a demonstration

git-svn-id: http://svn.openlayers.org/trunk/openlayers@2942 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
Tim Schaub
2007-03-31 16:21:14 +00:00
parent 7ab96241b0
commit 45da80f8ad
5 changed files with 609 additions and 0 deletions

146
examples/wkt.html Normal file
View File

@@ -0,0 +1,146 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>WKT</title>
<style type="text/css">
#info {
position: absolute;
top: 70px;
left: 550px;
width: 350px;
}
#map {
width: 512px;
height: 350px;
border: 1px solid gray;
}
#controls {
width: 512px;
}
#wktInput {
float: right;
}
#controlToggle {
padding-left: 1em;
}
#controlToggle li {
list-style: none;
}
</style>
<script src="../lib/OpenLayers.js"></script>
<script type="text/javascript">
<!--
var map, vectors, drawControls, wkt;
function init(){
map = new OpenLayers.Map('map');
var wms = new OpenLayers.Layer.WMS( "OpenLayers WMS",
"http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'});
vectors = new OpenLayers.Layer.Vector("Vector Layer");
map.addLayers([wms, vectors]);
map.addControl(new OpenLayers.Control.LayerSwitcher());
map.addControl(new OpenLayers.Control.MousePosition());
var options = {
hover: true,
onSelect: displayWKT
};
drawControls = {
point: new OpenLayers.Control.DrawFeature(vectors,
OpenLayers.Handler.Point),
line: new OpenLayers.Control.DrawFeature(vectors,
OpenLayers.Handler.Path),
polygon: new OpenLayers.Control.DrawFeature(vectors,
OpenLayers.Handler.Polygon),
hover: new OpenLayers.Control.SelectFeature(vectors, options)
};
for(var key in drawControls) {
map.addControl(drawControls[key]);
}
wkt = new OpenLayers.Format.WKT();
map.setCenter(new OpenLayers.LonLat(0, 0), 3);
document.getElementById('noneToggle').checked = true;
}
function toggleControl(element) {
for(key in drawControls) {
var control = drawControls[key];
if(element.value == key && element.checked) {
control.activate();
} else {
control.deactivate();
}
}
}
function displayWKT(geometry) {
var str = wkt.write(geometry);
// not a good idea in general, just for this demo
str = str.replace(/,/g, ', ');
document.getElementById('info').innerHTML = str;
}
function parseWKT() {
var element = document.getElementById('wkt');
var collection = wkt.read(element.value);
if(collection) {
if(collection.constructor != Array) {
collection = [collection];
}
var features = [];
for(var i=0; i<collection.length; ++i) {
features.push(new OpenLayers.Feature.Vector(collection[i]));
}
vectors.addFeatures(features);
var plural = (features.length > 1) ? 's' : '';
element.value = 'Feature' + plural + ' added'
} else {
element.value = 'Bad WKT';
}
}
// -->
</script>
</head>
<body onload="init()">
<h1>OpenLayers WKT Example</h1>
<div id="info"></div>
<div id="map"></div>
<div id="controls">
<p>See <a href="http://en.wikipedia.org/wiki/Well-known_text#Geometric_Objects">Wikipedia</a>
for a description and examples of WKT.</p>
<div id="wktInput">
<textarea id="wkt" rows="6" cols="30">paste WKT here...</textarea>
<br />
<input type="button" value="add feature" onclick="parseWKT();" />
</div>
<ul id="controlToggle">
<li>
<input type="radio" name="type" value="none" id="noneToggle"
onclick="toggleControl(this);" checked="checked" />
<label for="noneToggle">navigate</label>
</li>
<li>
<input type="radio" name="type" value="point" id="pointToggle" onclick="toggleControl(this);" />
<label for="pointToggle">draw point</label>
</li>
<li>
<input type="radio" name="type" value="line" id="lineToggle" onclick="toggleControl(this);" />
<label for="lineToggle">draw line</label>
</li>
<li>
<input type="radio" name="type" value="polygon" id="polygonToggle" onclick="toggleControl(this);" />
<label for="polygonToggle">draw polygon</label>
</li>
<li>
<input type="radio" name="type" value="hover" id="hoverToggle"
onclick="toggleControl(this);" />
<label for="hoverToggle">view WKT for feature</label>
</li>
</ul>
</div>
</body>
</html>

View File

@@ -143,6 +143,7 @@ if (typeof(_OPENLAYERS_SFL_) == "undefined") {
"OpenLayers/Format/KML.js",
"OpenLayers/Format/GeoRSS.js",
"OpenLayers/Format/WFS.js",
"OpenLayers/Format/WKT.js",
"OpenLayers/Layer/WFS.js",
"OpenLayers/Control/MouseToolbar.js",
"OpenLayers/Control/NavToolbar.js",

View File

@@ -0,0 +1,286 @@
/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
* See http://svn.openlayers.org/trunk/openlayers/repository-license.txt
* for the full text of the license. */
/**
* Read and write WKT.
* @requires OpenLayers/Format/JSON.js
*/
OpenLayers.Format.WKT = OpenLayers.Class.create();
OpenLayers.Format.WKT.prototype =
OpenLayers.Class.inherit(OpenLayers.Format, {
/**
*
*/
initialize: function(options) {
this.regExes = {
'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
'spaces': /\s+/,
'parenComma': /\)\s*,\s*\(/,
'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here
'trimParens': /^\s*\(?(.*?)\)?\s*$/
};
OpenLayers.Format.prototype.initialize.apply(this, [options]);
},
/**
* Deserialize a WKT string and return an OpenLayers.Geometry or an array
* of OpenLayers.Geometry. Supports WKT for POINT, MULTIPOINT, LINESTRING,
* MULTILINESTRING, POLYGON, MULTIPOLYGON, and GEOMETRYCOLLECTION.
* @param {String} wkt A WKT string
* @returns {OpenLayers.Geometry|Array} A geometry or array of geometries
* for GEOMETRYCOLLECTION WKT.
*/
read: function(wkt) {
var geometry, type, str;
var matches = this.regExes.typeStr.exec(wkt);
if(matches) {
type = matches[1].toLowerCase();
str = matches[2];
if(this.parse[type]) {
geometry = this.parse[type].apply(this, [str]);
}
}
return geometry;
},
/**
* Serialize a geometry or array of geometries into a WKT string.
* @param {OpenLayers.Geometry|Array} geom A geometry or array of geometries
* @returns {String} The WKT string representation of the input geometries
*/
write: function(geom) {
var collection, geometry, type, data, isCollection;
if(geom.constructor == Array) {
collection = geom;
isCollection = true;
} else {
collection = [geom];
isCollection = false;
}
var pieces = [];
if(isCollection) {
pieces.push('GEOMETRYCOLLECTION(');
}
for(var i=0; i<collection.length; ++i) {
if(isCollection && i>0) {
pieces.push(',');
}
geometry = collection[i];
type = geometry.CLASS_NAME.split('.')[2].toLowerCase();
data = this.extract[type].apply(this, [geometry]);
pieces.push(type.toUpperCase() + '(' + data + ')');
}
if(isCollection) {
pieces.push(')');
}
return pieces.join('');
},
/**
* Object with properties corresponding to the geometry types.
* Property values are functions that do the actual data extraction.
*/
extract: {
/**
* Return a space delimited string of point coordinates.
* @param {OpenLayers.Geometry.Point} point
* @returns {String} A string of coordinates representing the point
*/
'point': function(point) {
return point.x + ' ' + point.y;
},
/**
* Return a comma delimited string of point coordinates from a multipoint.
* @param {OpenLayers.Geometry.MultiPoint} multipoint
* @returns {String} A string of point coordinate strings representing
* the multipoint
*/
'multipoint': function(multipoint) {
var array = [];
for(var i=0; i<multipoint.components.length; ++i) {
array.push(this.extract.point.apply(this, [multipoint.components[i]]));
}
return array.join(',');
},
/**
* Return a comma delimited string of point coordinates from a line.
* @param {OpenLayers.Geometry.LineString} linestring
* @returns {String} A string of point coordinate strings representing
* the linestring
*/
'linestring': function(linestring) {
var array = [];
for(var i=0; i<linestring.components.length; ++i) {
array.push(this.extract.point.apply(this, [linestring.components[i]]));
}
return array.join(',');
},
/**
* Return a comma delimited string of linestring strings from a multilinestring.
* @param {OpenLayers.Geometry.MultiLineString} multilinestring
* @returns {String} A string of of linestring strings representing
* the multilinestring
*/
'multilinestring': function(multilinestring) {
var array = [];
for(var i=0; i<multilinestring.components.length; ++i) {
array.push('(' +
this.extract.linestring.apply(this, [multilinestring.components[i]]) +
')');
}
return array.join(',');
},
/**
* Return a comma delimited string of linear ring arrays from a polygon.
* @param {OpenLayers.Geometry.Polygon} polygon
* @returns {String} An array of linear ring arrays representing the polygon
*/
'polygon': function(polygon) {
var array = [];
for(var i=0; i<polygon.components.length; ++i) {
array.push('(' +
this.extract.linestring.apply(this, [polygon.components[i]]) +
')');
}
return array.join(',');
},
/**
* Return an array of polygon arrays from a multipolygon.
* @param {OpenLayers.Geometry.MultiPolygon} multipolygon
* @returns {Array} An array of polygon arrays representing
* the multipolygon
*/
'multipolygon': function(multipolygon) {
var array = [];
for(var i=0; i<multipolygon.components.length; ++i) {
array.push('(' +
this.extract.polygon.apply(this, [multipolygon.components[i]]) +
')');
}
return array.join(',');
}
},
/**
* Object with properties corresponding to the geometry types.
* Property values are functions that do the actual parsing.
*/
parse: {
/**
* Return point geometry given a point WKT fragment.
* @param {String} str A WKT fragment representing the point
* @returns {OpenLayers.Geometry.Point} A point geometry
*/
'point': function(str) {
var coords = str.trim().split(this.regExes.spaces);
return new OpenLayers.Geometry.Point(coords[0], coords[1]);
},
/**
* Return a multipoint geometry given a multipoint WKT fragment.
* @param {String} A WKT fragment representing the multipoint
* @returns {OpenLayers.Geometry.MultiPoint} A multipoint geometry
*/
'multipoint': function(str) {
var points = str.trim().split(',');
var components = [];
for(var i=0; i<points.length; ++i) {
components.push(this.parse.point.apply(this, [points[i]]));
}
return new OpenLayers.Geometry.MultiPoint(components);
},
/**
* Return a linestring geometry given a linestring WKT fragment.
* @param {String} A WKT fragment representing the linestring
* @returns {OpenLayers.Geometry.LineString} A linestring geometry
*/
'linestring': function(str) {
var points = str.trim().split(',');
var components = [];
for(var i=0; i<points.length; ++i) {
components.push(this.parse.point.apply(this, [points[i]]));
}
return new OpenLayers.Geometry.LineString(components);
},
/**
* Return a multilinestring geometry given a multilinestring WKT fragment.
* @param {String} A WKT fragment representing the multilinestring
* @returns {OpenLayers.Geometry.LineString} A multilinestring geometry
*/
'multilinestring': function(str) {
var line;
var lines = str.trim().split(this.regExes.parenComma);
var components = [];
for(var i=0; i<lines.length; ++i) {
line = lines[i].replace(this.regExes.trimParens, '$1');
components.push(this.parse.linestring.apply(this, [line]));
}
return new OpenLayers.Geometry.MultiLineString(components);
},
/**
* Return a polygon geometry given a polygon WKT fragment.
* @param {String} A WKT fragment representing the polygon
* @returns {OpenLayers.Geometry.Polygon} A polygon geometry
*/
'polygon': function(str) {
var ring, linestring, linearring;
var rings = str.trim().split(this.regExes.parenComma);
var components = [];
for(var i=0; i<rings.length; ++i) {
ring = rings[i].replace(this.regExes.trimParens, '$1');
linestring = this.parse.linestring.apply(this, [ring]);
linearring = new OpenLayers.Geometry.LinearRing(linestring.components);
components.push(linearring);
}
return new OpenLayers.Geometry.Polygon(components);
},
/**
* Return a multipolygon geometry given a multipolygon WKT fragment.
* @param {String} A WKT fragment representing the multipolygon
* @returns {OpenLayers.Geometry.MultiPolygon} A multipolygon geometry
*/
'multipolygon': function(str) {
var polygon;
var polygons = str.trim().split(this.regExes.doubleParenComma);
var components = [];
for(var i=0; i<polygons.length; ++i) {
polygon = polygons[i].replace(this.regExes.trimParens, '$1');
components.push(this.parse.polygon.apply(this, [polygon]));
}
return new OpenLayers.Geometry.MultiPolygon(components);
},
/**
* Return an array of geometries given a geometrycollection WKT fragment.
* @param {String} A WKT fragment representing the geometrycollection
* @returns {Array} An array of OpenLayers.Geometry
*/
'geometrycollection': function(str) {
// separate components of the collection with |
str = str.replace(/,\s*([A-Za-z])/g, '|$1');
var wktArray = str.trim().split('|');
var components = [];
for(var i=0; i<wktArray.length; ++i) {
components.push(OpenLayers.Format.WKT.prototype.read.apply(this,[wktArray[i]]));
}
return components;
}
},
/** @final @type String */
CLASS_NAME: "OpenLayers.Format.WKT"
});

174
tests/Format/test_WKT.html Normal file
View File

@@ -0,0 +1,174 @@
<html>
<head>
<script src="../../lib/OpenLayers.js"></script>
<script type="text/javascript"><!--
var points = [];
for(var i=0; i<12; ++i) {
points.push(new OpenLayers.Geometry.Point(Math.random() * 100,
Math.random() * 100));
}
var multipoint = new OpenLayers.Geometry.MultiPoint([
points[0], points[1], points[2]
]);
var linestrings = [
new OpenLayers.Geometry.LineString([points[0], points[1], points[2]]),
new OpenLayers.Geometry.LineString([points[3], points[4], points[5]])
];
var multilinestring = new OpenLayers.Geometry.MultiLineString([
linestrings[0], linestrings[1]
]);
var rings = [
new OpenLayers.Geometry.LinearRing([points[0], points[1], points[2]]),
new OpenLayers.Geometry.LinearRing([points[3], points[4], points[5]]),
new OpenLayers.Geometry.LinearRing([points[6], points[7], points[8]]),
new OpenLayers.Geometry.LinearRing([points[9], points[10], points[11]])
];
var polygons = [
new OpenLayers.Geometry.Polygon([rings[0], rings[1]]),
new OpenLayers.Geometry.Polygon([rings[2], rings[3]])
];
var multipolygon = new OpenLayers.Geometry.MultiPolygon([
polygons[0], polygons[1]
]);
var collection = [points[0], linestrings[0]];
function test_Format_WKT_constructor(t) {
t.plan(4);
var options = {'foo': 'bar'};
var format = new OpenLayers.Format.WKT(options);
t.ok(format instanceof OpenLayers.Format.WKT,
"new OpenLayers.Format.WKT 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_WKT_write(t) {
t.plan(7);
var format = new OpenLayers.Format.WKT();
// test a point
t.eq(format.write(points[0]),
"POINT(" + points[0].x + " " + points[0].y + ")",
"format correctly writes Point WKT");
// test a multipoint
t.eq(format.write(multipoint),
"MULTIPOINT(" + points[0].x + " " + points[0].y + "," +
points[1].x + " " + points[1].y + "," +
points[2].x + " " + points[2].y + ")",
"format correctly writes MultiPoint WKT");
// test a linestring
t.eq(format.write(linestrings[0]),
"LINESTRING(" + points[0].x + " " + points[0].y + "," +
points[1].x + " " + points[1].y + "," +
points[2].x + " " + points[2].y + ")",
"format correctly writes LineString WKT");
// test a multilinestring
t.eq(format.write(multilinestring),
"MULTILINESTRING((" + points[0].x + " " + points[0].y + "," +
points[1].x + " " + points[1].y + "," +
points[2].x + " " + points[2].y + ")," +
"(" + points[3].x + " " + points[3].y + "," +
points[4].x + " " + points[4].y + "," +
points[5].x + " " + points[5].y + "))",
"format correctly writes MultiLineString WKT");
// test a polygon
t.eq(format.write(polygons[0]),
"POLYGON((" + points[0].x + " " + points[0].y + "," +
points[1].x + " " + points[1].y + "," +
points[2].x + " " + points[2].y + "," +
points[0].x + " " + points[0].y + ")," +
"(" + points[3].x + " " + points[3].y + "," +
points[4].x + " " + points[4].y + "," +
points[5].x + " " + points[5].y + "," +
points[3].x + " " + points[3].y + "))",
"format correctly writes Polygon WKT");
// test a multipolygon
t.eq(format.write(multipolygon),
"MULTIPOLYGON(((" + points[0].x + " " + points[0].y + "," +
points[1].x + " " + points[1].y + "," +
points[2].x + " " + points[2].y + "," +
points[0].x + " " + points[0].y + ")," +
"(" + points[3].x + " " + points[3].y + "," +
points[4].x + " " + points[4].y + "," +
points[5].x + " " + points[5].y + "," +
points[3].x + " " + points[3].y + "))," +
"((" + points[6].x + " " + points[6].y + "," +
points[7].x + " " + points[7].y + "," +
points[8].x + " " + points[8].y + "," +
points[6].x + " " + points[6].y + ")," +
"(" + points[9].x + " " + points[9].y + "," +
points[10].x + " " + points[10].y + "," +
points[11].x + " " + points[11].y + "," +
points[9].x + " " + points[9].y + ")))",
"format correctly writes MultiPolygon WKT");
// test a geometrycollection
t.eq(format.write(collection),
"GEOMETRYCOLLECTION(POINT(" + points[0].x + " " + points[0].y + ")," +
"LINESTRING(" + points[0].x + " " + points[0].y + "," +
points[1].x + " " + points[1].y + "," +
points[2].x + " " + points[2].y + "))",
"format correctly writes GeometryCollection WKT");
}
function test_Format_WKT_read(t) {
t.plan(7);
var format = new OpenLayers.Format.WKT();
/**
* Since we're explicitly testing calls to write, the read tests
* just make sure that geometry can make a round trip from read to write.
*/
// test a point
t.ok(points[0].equals(format.read(format.write(points[0]))),
"format correctly reads Point WKT");
// test a multipoint
t.ok(multipoint.equals(format.read(format.write(multipoint))),
"format correctly reads MultiPoint WKT");
// test a linestring
t.ok(linestrings[0].equals(format.read(format.write(linestrings[0]))),
"format correctly reads LineString WKT");
// test a multilinestring
t.ok(multilinestring.equals(format.read(format.write(multilinestring))),
"format correctly reads MultiLineString WKT");
// test a polygon
t.ok(polygons[0].equals(format.read(format.write(polygons[0]))),
"format correctly reads Polygon WKT");
// test a multipolygon
t.ok(multipolygon.equals(format.read(format.write(multipolygon))),
"format correctly reads MultiPolygon WKT");
// test a geometrycollection
t.eq(format.write(collection),
"GEOMETRYCOLLECTION(POINT(" + points[0].x + " " + points[0].y + ")," +
"LINESTRING(" + points[0].x + " " + points[0].y + "," +
points[1].x + " " + points[1].y + "," +
points[2].x + " " + points[2].y + "))",
"format correctly writes GeometryCollection WKT");
}
// -->
</script>
</head>
<body>
</body>
</html>

View File

@@ -16,6 +16,8 @@
<li>Geometry/test_MultiPolygon.html</li>
<li>Geometry/test_Rectangle.html</li>
<li>Geometry/test_Surface.html</li>
<li>test_Format.html</li>
<li>Format/test_WKT.html</li>
<li>test_Icon.html</li>
<li>test_Marker.html</li>
<li>test_Popup.html</li>