More trip planner testing with colors
[busui.git] / labs / openlayers / lib / OpenLayers / Format / GeoJSON.js
blob:a/labs/openlayers/lib/OpenLayers/Format/GeoJSON.js -> blob:b/labs/openlayers/lib/OpenLayers/Format/GeoJSON.js
  /* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for
  * full list of contributors). Published under the Clear BSD license.
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
   
  /**
  * @requires OpenLayers/Format/JSON.js
  * @requires OpenLayers/Feature/Vector.js
  * @requires OpenLayers/Geometry/Point.js
  * @requires OpenLayers/Geometry/MultiPoint.js
  * @requires OpenLayers/Geometry/LineString.js
  * @requires OpenLayers/Geometry/MultiLineString.js
  * @requires OpenLayers/Geometry/Polygon.js
  * @requires OpenLayers/Geometry/MultiPolygon.js
  * @requires OpenLayers/Console.js
  */
   
  /**
  * Class: OpenLayers.Format.GeoJSON
  * Read and write GeoJSON. Create a new parser with the
  * <OpenLayers.Format.GeoJSON> constructor.
  *
  * Inherits from:
  * - <OpenLayers.Format.JSON>
  */
  OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {
   
  /**
  * APIProperty: ignoreExtraDims
  * {Boolean} Ignore dimensions higher than 2 when reading geometry
  * coordinates.
  */
  ignoreExtraDims: false,
   
  /**
  * Constructor: OpenLayers.Format.GeoJSON
  * Create a new parser for GeoJSON.
  *
  * Parameters:
  * options - {Object} An optional object whose properties will be set on
  * this instance.
  */
  initialize: function(options) {
  OpenLayers.Format.JSON.prototype.initialize.apply(this, [options]);
  },
   
  /**
  * APIMethod: read
  * Deserialize a GeoJSON string.
  *
  * Parameters:
  * json - {String} A GeoJSON string
  * type - {String} Optional string that determines the structure of
  * the output. Supported values are "Geometry", "Feature", and
  * "FeatureCollection". If absent or null, a default of
  * "FeatureCollection" is assumed.
  * filter - {Function} A function which will be called for every key and
  * value at every level of the final result. Each value will be
  * replaced by the result of the filter function. This can be used to
  * reform generic objects into instances of classes, or to transform
  * date strings into Date objects.
  *
  * Returns:
  * {Object} The return depends on the value of the type argument. If type
  * is "FeatureCollection" (the default), the return will be an array
  * of <OpenLayers.Feature.Vector>. If type is "Geometry", the input json
  * must represent a single geometry, and the return will be an
  * <OpenLayers.Geometry>. If type is "Feature", the input json must
  * represent a single feature, and the return will be an
  * <OpenLayers.Feature.Vector>.
  */
  read: function(json, type, filter) {
  type = (type) ? type : "FeatureCollection";
  var results = null;
  var obj = null;
  if (typeof json == "string") {
  obj = OpenLayers.Format.JSON.prototype.read.apply(this,
  [json, filter]);
  } else {
  obj = json;
  }
  if(!obj) {
  OpenLayers.Console.error("Bad JSON: " + json);
  } else if(typeof(obj.type) != "string") {
  OpenLayers.Console.error("Bad GeoJSON - no type: " + json);
  } else if(this.isValidType(obj, type)) {
  switch(type) {
  case "Geometry":
  try {
  results = this.parseGeometry(obj);
  } catch(err) {
  OpenLayers.Console.error(err);
  }
  break;
  case "Feature":
  try {
  results = this.parseFeature(obj);
  results.type = "Feature";
  } catch(err) {
  OpenLayers.Console.error(err);
  }
  break;
  case "FeatureCollection":
  // for type FeatureCollection, we allow input to be any type
  results = [];
  switch(obj.type) {
  case "Feature":
  try {
  results.push(this.parseFeature(obj));
  } catch(err) {
  results = null;
  OpenLayers.Console.error(err);
  }
  break;
  case "FeatureCollection":
  for(var i=0, len=obj.features.length; i<len; ++i) {
  try {
  results.push(this.parseFeature(obj.features[i]));
  } catch(err) {
  results = null;
  OpenLayers.Console.error(err);
  }
  }
  break;
  default:
  try {
  var geom = this.parseGeometry(obj);
  results.push(new OpenLayers.Feature.Vector(geom));
  } catch(err) {
  results = null;
  OpenLayers.Console.error(err);
  }
  }
  break;
  }
  }
  return results;
  },
   
  /**
  * Method: isValidType
  * Check if a GeoJSON object is a valid representative of the given type.
  *
  * Returns:
  * {Boolean} The object is valid GeoJSON object of the given type.
  */
  isValidType: function(obj, type) {
  var valid = false;
  switch(type) {
  case "Geometry":
  if(OpenLayers.Util.indexOf(
  ["Point", "MultiPoint", "LineString", "MultiLineString",
  "Polygon", "MultiPolygon", "Box", "GeometryCollection"],
  obj.type) == -1) {
  // unsupported geometry type
  OpenLayers.Console.error("Unsupported geometry type: " +
  obj.type);
  } else {
  valid = true;
  }
  break;
  case "FeatureCollection":
  // allow for any type to be converted to a feature collection
  valid = true;
  break;
  default:
  // for Feature types must match
  if(obj.type == type) {
  valid = true;
  } else {
  OpenLayers.Console.error("Cannot convert types from " +
  obj.type + " to " + type);
  }
  }
  return valid;
  },
   
  /**
  * Method: parseFeature
  * Convert a feature object from GeoJSON into an
  * <OpenLayers.Feature.Vector>.
  *
  * Parameters:
  * obj - {Object} An object created from a GeoJSON object
  *
  * Returns:
  * {<OpenLayers.Feature.Vector>} A feature.
  */
  parseFeature: function(obj) {
  var feature, geometry, attributes, bbox;
  attributes = (obj.properties) ? obj.properties : {};
  bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox;
  try {
  geometry = this.parseGeometry(obj.geometry);
  } catch(err) {
  // deal with bad geometries
  throw err;
  }
  feature = new OpenLayers.Feature.Vector(geometry, attributes);
  if(bbox) {
  feature.bounds = OpenLayers.Bounds.fromArray(bbox);
  }
  if(obj.id) {
  feature.fid = obj.id;
  }
  return feature;
  },
   
  /**
  * Method: parseGeometry
  * Convert a geometry object from GeoJSON into an <OpenLayers.Geometry>.
  *
  * Parameters:
  * obj - {Object} An object created from a GeoJSON object
  *
  * Returns:
  * {<OpenLayers.Geometry>} A geometry.
  */
  parseGeometry: function(obj) {
  if (obj == null) {
  return null;
  }
  var geometry, collection = false;
  if(obj.type == "GeometryCollection") {
  if(!(obj.geometries instanceof Array)) {
  throw "GeometryCollection must have geometries array: " + obj;
  }
  var numGeom = obj.geometries.length;
  var components = new Array(numGeom);
  for(var i=0; i<numGeom; ++i) {
  components[i] = this.parseGeometry.apply(
  this, [obj.geometries[i]]
  );
  }
  geometry = new OpenLayers.Geometry.Collection(components);
  collection = true;
  } else {
  if(!(obj.coordinates instanceof Array)) {
  throw "Geometry must have coordinates array: " + obj;
  }
  if(!this.parseCoords[obj.type.toLowerCase()]) {
  throw "Unsupported geometry type: " + obj.type;
  }
  try {
  geometry = this.parseCoords[obj.type.toLowerCase()].apply(
  this, [obj.coordinates]
  );
  } catch(err) {
  // deal with bad coordinates
  throw err;
  }
  }
  // We don't reproject collections because the children are reprojected
  // for us when they are created.
  if (this.internalProjection && this.externalProjection && !collection) {
  geometry.transform(this.externalProjection,
  this.internalProjection);
  }
  return geometry;
  },
   
  /**
  * Property: parseCoords
  * Object with properties corresponding to the GeoJSON geometry types.
  * Property values are functions that do the actual parsing.
  */
  parseCoords: {
  /**
  * Method: parseCoords.point
  * Convert a coordinate array from GeoJSON into an
  * <OpenLayers.Geometry>.
  *
  * Parameters:
  * array - {Object} The coordinates array from the GeoJSON fragment.
  *
  * Returns:
  * {<OpenLayers.Geometry>} A geometry.
  */
  "point": function(array) {
  if (this.ignoreExtraDims == false &&
  array.length != 2) {
  throw "Only 2D points are supported: " + array;
  }
  return new OpenLayers.Geometry.Point(array[0], array[1]);
  },
   
  /**
  * Method: parseCoords.multipoint
  * Convert a coordinate array from GeoJSON into an
  * <OpenLayers.Geometry>.
  *
  * Parameters:
  * array {Object} The coordinates array from the GeoJSON fragment.
  *
  * Returns:
  * {<OpenLayers.Geometry>} A geometry.
  */
  "multipoint": function(array) {
  var points = [];
  var p = null;
  for(var i=0, len=array.length; i<len; ++i) {
  try {
  p = this.parseCoords["point"].apply(this, [array[i]]);
  } catch(err) {
  throw err;
  }
  points.push(p);
  }
  return new OpenLayers.Geometry.MultiPoint(points);
  },
   
  /**
  * Method: parseCoords.linestring
  * Convert a coordinate array from GeoJSON into an
  * <OpenLayers.Geometry>.
  *
  * Parameters:
  * array - {Object} The coordinates array from the GeoJSON fragment.
  *
  * Returns:
  * {<OpenLayers.Geometry>} A geometry.
  */
  "linestring": function(array) {
  var points = [];
  var p = null;
  for(var i=0, len=array.length; i<len; ++i) {
  try {
  p = this.parseCoords["point"].apply(this, [array[i]]);
  } catch(err) {
  throw err;
  }
  points.push(p);
  }
  return new OpenLayers.Geometry.LineString(points);
  },
   
  /**
  * Method: parseCoords.multilinestring
  * Convert a coordinate array from GeoJSON into an
  * <OpenLayers.Geometry>.
  *
  * Parameters:
  * array - {Object} The coordinates array from the GeoJSON fragment.
  *
  * Returns:
  * {<OpenLayers.Geometry>} A geometry.
  */
  "multilinestring": function(array) {
  var lines = [];
  var l = null;
  for(var i=0, len=array.length; i<len; ++i) {
  try {
  l = this.parseCoords["linestring"].apply(this, [array[i]]);
  } catch(err) {
  throw err;
  }
  lines.push(l);
  }
  return new OpenLayers.Geometry.MultiLineString(lines);
  },
   
  /**
  * Method: parseCoords.polygon
  * Convert a coordinate array from GeoJSON into an
  * <OpenLayers.Geometry>.
  *