1 /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
2 * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
3 * full text of the license. */
6 * @requires OpenLayers/Format/JSON.js
7 * @requires OpenLayers/Feature/Vector.js
8 * @requires OpenLayers/Geometry/Point.js
9 * @requires OpenLayers/Geometry/MultiPoint.js
10 * @requires OpenLayers/Geometry/LineString.js
11 * @requires OpenLayers/Geometry/MultiLineString.js
12 * @requires OpenLayers/Geometry/Polygon.js
13 * @requires OpenLayers/Geometry/MultiPolygon.js
14 * @requires OpenLayers/Console.js
18 * Class: OpenLayers.Format.GeoJSON
19 * Read and write GeoJSON. Create a new parser with the
20 * <OpenLayers.Format.GeoJSON> constructor.
23 * - <OpenLayers.Format.JSON>
25 OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {
28 * Constructor: OpenLayers.Format.GeoJSON
29 * Create a new parser for GeoJSON.
32 * options - {Object} An optional object whose properties will be set on
35 initialize: function(options) {
36 OpenLayers.Format.JSON.prototype.initialize.apply(this, [options]);
41 * Deserialize a GeoJSON string.
44 * json - {String} A GeoJSON string
45 * type - {String} Optional string that determines the structure of
46 * the output. Supported values are "Geometry", "Feature", and
47 * "FeatureCollection". If absent or null, a default of
48 * "FeatureCollection" is assumed.
49 * filter - {Function} A function which will be called for every key and
50 * value at every level of the final result. Each value will be
51 * replaced by the result of the filter function. This can be used to
52 * reform generic objects into instances of classes, or to transform
53 * date strings into Date objects.
56 * {Object} The return depends on the value of the type argument. If type
57 * is "FeatureCollection" (the default), the return will be an array
58 * of <OpenLayers.Feature.Vector>. If type is "Geometry", the input json
59 * must represent a single geometry, and the return will be an
60 * <OpenLayers.Geometry>. If type is "Feature", the input json must
61 * represent a single feature, and the return will be an
62 * <OpenLayers.Feature.Vector>.
64 read: function(json, type, filter) {
65 type = (type) ? type : "FeatureCollection";
68 if (typeof json == "string") {
69 obj = OpenLayers.Format.JSON.prototype.read.apply(this,
75 OpenLayers.Console.error("Bad JSON: " + json);
76 } else if(typeof(obj.type) != "string") {
77 OpenLayers.Console.error("Bad GeoJSON - no type: " + json);
78 } else if(this.isValidType(obj, type)) {
82 results = this.parseGeometry(obj);
84 OpenLayers.Console.error(err);
89 results = this.parseFeature(obj);
90 results.type = "Feature";
92 OpenLayers.Console.error(err);
95 case "FeatureCollection":
96 // for type FeatureCollection, we allow input to be any type
101 results.push(this.parseFeature(obj));
104 OpenLayers.Console.error(err);
107 case "FeatureCollection":
108 for(var i=0, len=obj.features.length; i<len; ++i) {
110 results.push(this.parseFeature(obj.features[i]));
113 OpenLayers.Console.error(err);
119 var geom = this.parseGeometry(obj);
120 results.push(new OpenLayers.Feature.Vector(geom));
123 OpenLayers.Console.error(err);
133 * Method: isValidType
134 * Check if a GeoJSON object is a valid representative of the given type.
137 * {Boolean} The object is valid GeoJSON object of the given type.
139 isValidType: function(obj, type) {
143 if(OpenLayers.Util.indexOf(
144 ["Point", "MultiPoint", "LineString", "MultiLineString",
145 "Polygon", "MultiPolygon", "Box", "GeometryCollection"],
147 // unsupported geometry type
148 OpenLayers.Console.error("Unsupported geometry type: " +
154 case "FeatureCollection":
155 // allow for any type to be converted to a feature collection
159 // for Feature types must match
160 if(obj.type == type) {
163 OpenLayers.Console.error("Cannot convert types from " +
164 obj.type + " to " + type);
171 * Method: parseFeature
172 * Convert a feature object from GeoJSON into an
173 * <OpenLayers.Feature.Vector>.
176 * obj - {Object} An object created from a GeoJSON object
179 * {<OpenLayers.Feature.Vector>} A feature.
181 parseFeature: function(obj) {
182 var feature, geometry, attributes, bbox;
183 attributes = (obj.properties) ? obj.properties : {};
184 bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox;
186 geometry = this.parseGeometry(obj.geometry);
188 // deal with bad geometries
191 feature = new OpenLayers.Feature.Vector(geometry, attributes);
193 feature.bounds = OpenLayers.Bounds.fromArray(bbox);
196 feature.fid = obj.id;
202 * Method: parseGeometry
203 * Convert a geometry object from GeoJSON into an <OpenLayers.Geometry>.
206 * obj - {Object} An object created from a GeoJSON object
209 * {<OpenLayers.Geometry>} A geometry.
211 parseGeometry: function(obj) {
215 var geometry, collection = false;
216 if(obj.type == "GeometryCollection") {
217 if(!(obj.geometries instanceof Array)) {
218 throw "GeometryCollection must have geometries array: " + obj;
220 var numGeom = obj.geometries.length;
221 var components = new Array(numGeom);
222 for(var i=0; i<numGeom; ++i) {
223 components[i] = this.parseGeometry.apply(
224 this, [obj.geometries[i]]
227 geometry = new OpenLayers.Geometry.Collection(components);
230 if(!(obj.coordinates instanceof Array)) {
231 throw "Geometry must have coordinates array: " + obj;
233 if(!this.parseCoords[obj.type.toLowerCase()]) {
234 throw "Unsupported geometry type: " + obj.type;
237 geometry = this.parseCoords[obj.type.toLowerCase()].apply(
238 this, [obj.coordinates]
241 // deal with bad coordinates
245 // We don't reproject collections because the children are reprojected
246 // for us when they are created.
247 if (this.internalProjection && this.externalProjection && !collection) {
248 geometry.transform(this.externalProjection,
249 this.internalProjection);
255 * Property: parseCoords
256 * Object with properties corresponding to the GeoJSON geometry types.
257 * Property values are functions that do the actual parsing.
261 * Method: parseCoords.point
262 * Convert a coordinate array from GeoJSON into an
263 * <OpenLayers.Geometry>.
266 * array - {Object} The coordinates array from the GeoJSON fragment.
269 * {<OpenLayers.Geometry>} A geometry.
271 "point": function(array) {
272 if(array.length != 2) {
273 throw "Only 2D points are supported: " + array;
275 return new OpenLayers.Geometry.Point(array[0], array[1]);
279 * Method: parseCoords.multipoint
280 * Convert a coordinate array from GeoJSON into an
281 * <OpenLayers.Geometry>.
284 * array {Object} The coordinates array from the GeoJSON fragment.
287 * {<OpenLayers.Geometry>} A geometry.
289 "multipoint": function(array) {
292 for(var i=0, len=array.length; i<len; ++i) {
294 p = this.parseCoords["point"].apply(this, [array[i]]);
300 return new OpenLayers.Geometry.MultiPoint(points);
304 * Method: parseCoords.linestring
305 * Convert a coordinate array from GeoJSON into an
306 * <OpenLayers.Geometry>.
309 * array - {Object} The coordinates array from the GeoJSON fragment.
312 * {<OpenLayers.Geometry>} A geometry.
314 "linestring": function(array) {
317 for(var i=0, len=array.length; i<len; ++i) {
319 p = this.parseCoords["point"].apply(this, [array[i]]);
325 return new OpenLayers.Geometry.LineString(points);
329 * Method: parseCoords.multilinestring
330 * Convert a coordinate array from GeoJSON into an
331 * <OpenLayers.Geometry>.
334 * array - {Object} The coordinates array from the GeoJSON fragment.
337 * {<OpenLayers.Geometry>} A geometry.
339 "multilinestring": function(array) {
342 for(var i=0, len=array.length; i<len; ++i) {
344 l = this.parseCoords["linestring"].apply(this, [array[i]]);
350 return new OpenLayers.Geometry.MultiLineString(lines);
354 * Method: parseCoords.polygon
355 * Convert a coordinate array from GeoJSON into an
356 * <OpenLayers.Geometry>.
359 * {<OpenLayers.Geometry>} A geometry.
361 "polygon": function(array) {
364 for(var i=0, len=array.length; i<len; ++i) {
366 l = this.parseCoords["linestring"].apply(this, [array[i]]);
370 r = new OpenLayers.Geometry.LinearRing(l.components);
373 return new OpenLayers.Geometry.Polygon(rings);
377 * Method: parseCoords.multipolygon
378 * Convert a coordinate array from GeoJSON into an
379 * <OpenLayers.Geometry>.
382 * array - {Object} The coordinates array from the GeoJSON fragment.
385 * {<OpenLayers.Geometry>} A geometry.
387 "multipolygon": function(array) {
390 for(var i=0, len=array.length; i<len; ++i) {
392 p = this.parseCoords["polygon"].apply(this, [array[i]]);
398 return new OpenLayers.Geometry.MultiPolygon(polys);
402 * Method: parseCoords.box
403 * Convert a coordinate array from GeoJSON into an
404 * <OpenLayers.Geometry>.
407 * array - {Object} The coordinates array from the GeoJSON fragment.
410 * {<OpenLayers.Geometry>} A geometry.
412 "box": function(array) {
413 if(array.length != 2) {
414 throw "GeoJSON box coordinates must have 2 elements";
416 return new OpenLayers.Geometry.Polygon([
417 new OpenLayers.Geometry.LinearRing([
418 new OpenLayers.Geometry.Point(array[0][0], array[0][1]),
419 new OpenLayers.Geometry.Point(array[1][0], array[0][1]),
420 new OpenLayers.Geometry.Point(array[1][0], array[1][1]),
421 new OpenLayers.Geometry.Point(array[0][0], array[1][1]),
422 new OpenLayers.Geometry.Point(array[0][0], array[0][1])
431 * Serialize a feature, geometry, array of features into a GeoJSON string.
434 * obj - {Object} An <OpenLayers.Feature.Vector>, <OpenLayers.Geometry>,
435 * or an array of features.
436 * pretty - {Boolean} Structure the output with newlines and indentation.
440 * {String} The GeoJSON string representation of the input geometry,
441 * features, or array of features.
443 write: function(obj, pretty) {
447 if(obj instanceof Array) {
448 geojson.type = "FeatureCollection";
449 var numFeatures = obj.length;
450 geojson.features = new Array(numFeatures);
451 for(var i=0; i<numFeatures; ++i) {
452 var element = obj[i];
453 if(!element instanceof OpenLayers.Feature.Vector) {
454 var msg = "FeatureCollection only supports collections " +
455 "of features: " + element;
458 geojson.features[i] = this.extract.feature.apply(
462 } else if (obj.CLASS_NAME.indexOf("OpenLayers.Geometry") == 0) {
463 geojson = this.extract.geometry.apply(this, [obj]);
464 } else if (obj instanceof OpenLayers.Feature.Vector) {
465 geojson = this.extract.feature.apply(this, [obj]);
466 if(obj.layer && obj.layer.projection) {
467 geojson.crs = this.createCRSObject(obj);
470 return OpenLayers.Format.JSON.prototype.write.apply(this,
475 * Method: createCRSObject
476 * Create the CRS object for an object.
479 * object - {<OpenLayers.Feature.Vector>}
482 * {Object} An object which can be assigned to the crs property
483 * of a GeoJSON object.
485 createCRSObject: function(object) {
486 var proj = object.layer.projection.toString();
488 if (proj.match(/epsg:/i)) {
489 var code = parseInt(proj.substring(proj.indexOf(":") + 1));
494 "urn": "urn:ogc:def:crs:OGC:1.3:CRS84"
511 * Object with properties corresponding to the GeoJSON types.
512 * Property values are functions that do the actual value extraction.
516 * Method: extract.feature
517 * Return a partial GeoJSON object representing a single feature.
520 * feature - {<OpenLayers.Feature.Vector>}
523 * {Object} An object representing the point.
525 'feature': function(feature) {
526 var geom = this.extract.geometry.apply(this, [feature.geometry]);
529 "id": feature.fid == null ? feature.id : feature.fid,
530 "properties": feature.attributes,
536 * Method: extract.geometry
537 * Return a GeoJSON object representing a single geometry.
540 * geometry - {<OpenLayers.Geometry>}
543 * {Object} An object representing the geometry.
545 'geometry': function(geometry) {
546 if (geometry == null) {
549 if (this.internalProjection && this.externalProjection) {
550 geometry = geometry.clone();
551 geometry.transform(this.internalProjection,
552 this.externalProjection);
554 var geometryType = geometry.CLASS_NAME.split('.')[2];
555 var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);
557 if(geometryType == "Collection") {
559 "type": "GeometryCollection",
564 "type": geometryType,
573 * Method: extract.point
574 * Return an array of coordinates from a point.
577 * point - {<OpenLayers.Geometry.Point>}
580 * {Array} An array of coordinates representing the point.
582 'point': function(point) {
583 return [point.x, point.y];
587 * Method: extract.multipoint
588 * Return an array of point coordinates from a multipoint.
591 * multipoint - {<OpenLayers.Geometry.MultiPoint>}
594 * {Array} An array of point coordinate arrays representing
597 'multipoint': function(multipoint) {
599 for(var i=0, len=multipoint.components.length; i<len; ++i) {
600 array.push(this.extract.point.apply(this, [multipoint.components[i]]));
606 * Method: extract.linestring
607 * Return an array of coordinate arrays from a linestring.
610 * linestring - {<OpenLayers.Geometry.LineString>}
613 * {Array} An array of coordinate arrays representing
616 'linestring': function(linestring) {
618 for(var i=0, len=linestring.components.length; i<len; ++i) {
619 array.push(this.extract.point.apply(this, [linestring.components[i]]));
625 * Method: extract.multilinestring
626 * Return an array of linestring arrays from a linestring.
629 * linestring - {<OpenLayers.Geometry.MultiLineString>}
632 * {Array} An array of linestring arrays representing
633 * the multilinestring.
635 'multilinestring': function(multilinestring) {
637 for(var i=0, len=multilinestring.components.length; i<len; ++i) {
638 array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]));
644 * Method: extract.polygon
645 * Return an array of linear ring arrays from a polygon.
648 * polygon - {<OpenLayers.Geometry.Polygon>}
651 * {Array} An array of linear ring arrays representing the polygon.
653 'polygon': function(polygon) {
655 for(var i=0, len=polygon.components.length; i<len; ++i) {
656 array.push(this.extract.linestring.apply(this, [polygon.components[i]]));
662 * Method: extract.multipolygon
663 * Return an array of polygon arrays from a multipolygon.
666 * multipolygon - {<OpenLayers.Geometry.MultiPolygon>}
669 * {Array} An array of polygon arrays representing
672 'multipolygon': function(multipolygon) {
674 for(var i=0, len=multipolygon.components.length; i<len; ++i) {
675 array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]));
681 * Method: extract.collection
682 * Return an array of geometries from a geometry collection.
685 * collection - {<OpenLayers.Geometry.Collection>}
688 * {Array} An array of geometry objects representing the geometry
691 'collection': function(collection) {
692 var len = collection.components.length;
693 var array = new Array(len);
694 for(var i=0; i<len; ++i) {
695 array[i] = this.extract.geometry.apply(
696 this, [collection.components[i]]
705 CLASS_NAME: "OpenLayers.Format.GeoJSON"