Files
openlayers/lib/OpenLayers/Format/GML.js
crschmidt 3948913bfc Merge all changes from the naturaldocs sandbox. This brings all the work that
has been done in the NaturalDocs branch back to trunk. Thanks to everyone who
helped out in making this happen. (I could list people, but the list would
be long, and I'm already mentally on vacation.)


git-svn-id: http://svn.openlayers.org/trunk/openlayers@3545 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
2007-06-29 15:59:20 +00:00

499 lines
19 KiB
JavaScript

/* 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. */
/**
* @requires OpenLayers/Format.js
* @requires OpenLayers/Feature/Vector.js
* @requires OpenLayers/Ajax.js
* @requires OpenLayers/Geometry.js
*
* Class: OpenLayers.Format.GML
* Read/Wite GML.
*
* Inherits from:
* - <OpenLayers.Format>
*/
OpenLayers.Format.GML = OpenLayers.Class.create();
OpenLayers.Format.GML.prototype =
OpenLayers.Class.inherit( OpenLayers.Format, {
/*
* APIProperty: featureNS
* Namespace used for feature attributes. Default matches the NS
* used by MapServer output.
*/
featureNS: "http://mapserver.gis.umn.edu/mapserver",
/*
* APIProperty: featureName
* element name for features. Default is 'featureMember'.
*/
featureName: "featureMember",
/*
* APIProperty: layerName
* Name of data layer. Default is 'features'.
*/
layerName: "features",
/**
* APIProperty: geometry
* Name of geometry element.
*/
geometryName: "geometry",
/**
* APIProperty: collectionName
* Name of featureCollection element
*/
collectionName: "FeatureCollection",
/**
* APIProperty: gmlns
* GML Namespace
*/
gmlns: "http://www.opengis.net/gml",
/**
* APIProperty: extractAttributes
* {Boolean} Extract attributes from GML. Most of the time, this is a
* significant time usage, due to the need to recursively descend the XML
* to search for attributes.
*/
extractAttributes: true,
/**
* APIMethod: read
* Read data from a string, and return a list of features.
*
* Parameters:
* data - {String} or {XMLNode} data to read/parse.
*/
read: function(data) {
if (typeof data == "string") {
data = OpenLayers.parseXMLString(data);
}
var featureNodes = OpenLayers.Ajax.getElementsByTagNameNS(data, this.gmlns, "gml", this.featureName);
if (featureNodes.length == 0) { return []; }
// Determine dimension of the FeatureCollection. Ie, dim=2 means (x,y) coords
// dim=3 means (x,y,z) coords
// GML3 can have 2 or 3 dimensions. GML2 only 2.
var dim;
var coordNodes = OpenLayers.Ajax.getElementsByTagNameNS(featureNodes[0], this.gmlns, "gml", "posList");
if (coordNodes.length == 0) {
coordNodes = OpenLayers.Ajax.getElementsByTagNameNS(featureNodes[0], this.gmlns, "gml", "pos");
}
if (coordNodes.length > 0) {
dim = coordNodes[0].getAttribute("srsDimension");
}
this.dim = (dim == "3" || dim == 3) ? 3 : 2;
var features = [];
// Process all the featureMembers
for (var i = 0; i < featureNodes.length; i++) {
var feature = this.parseFeature(featureNodes[i]);
if (feature) {
features.push(feature);
}
}
return features;
},
/**
* Method: parseFeature
* This function is the core of the GML parsing code in OpenLayers.
* It creates the geometries that are then attached to the returned
* feature, and calls parseAttributes() to get attribute data out.
* Parameters:
* xmlNode - {<DOMElement>}
*/
parseFeature: function(xmlNode) {
var geom;
var p; // [points,bounds]
var feature = new OpenLayers.Feature.Vector();
// match MultiPolygon
if (OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, this.gmlns, "gml", "MultiPolygon").length != 0) {
var multipolygon = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, this.gmlns, "gml", "MultiPolygon")[0];
feature.fid = multipolygon.parentNode.parentNode.getAttribute('fid');
geom = new OpenLayers.Geometry.MultiPolygon();
var polygons = OpenLayers.Ajax.getElementsByTagNameNS(multipolygon,
this.gmlns, "gml", "Polygon");
for (var i = 0; i < polygons.length; i++) {
polygon = this.parsePolygonNode(polygons[i],geom);
geom.addComponents(polygon);
}
}
// match MultiLineString
else if (OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
this.gmlns, "gml", "MultiLineString").length != 0) {
var multilinestring = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
this.gmlns, "gml", "MultiLineString")[0];
feature.fid = multilinestring.parentNode.parentNode.getAttribute('fid');
geom = new OpenLayers.Geometry.MultiLineString();
var lineStrings = OpenLayers.Ajax.getElementsByTagNameNS(multilinestring, this.gmlns, "gml", "LineString");
for (var i = 0; i < lineStrings.length; i++) {
p = this.parseCoords(lineStrings[i]);
if(p.points){
var lineString = new OpenLayers.Geometry.LineString(p.points);
geom.addComponents(lineString);
// TBD Bounds only set for one of multiple geometries
}
}
}
// match MultiPoint
else if (OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
this.gmlns, "gml", "MultiPoint").length != 0) {
var multiPoint = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
this.gmlns, "gml", "MultiPoint")[0];
feature.fid = multiPoint.parentNode.parentNode.getAttribute('fid');
geom = new OpenLayers.Geometry.MultiPoint();
var points = OpenLayers.Ajax.getElementsByTagNameNS(multiPoint, this.gmlns, "gml", "Point");
for (var i = 0; i < points.length; i++) {
p = this.parseCoords(points[i]);
geom.addComponents(p.points[0]);
// TBD Bounds only set for one of multiple geometries
}
}
// match Polygon
else if (OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
this.gmlns, "gml", "Polygon").length != 0) {
var polygon = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
this.gmlns, "gml", "Polygon")[0];
feature.fid = polygon.parentNode.parentNode.getAttribute('fid');
geom = this.parsePolygonNode(polygon);
}
// match LineString
else if (OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
this.gmlns, "gml", "LineString").length != 0) {
var lineString = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
this.gmlns, "gml", "LineString")[0];
feature.fid = lineString.parentNode.parentNode.getAttribute('fid');
p = this.parseCoords(lineString);
if (p.points) {
geom = new OpenLayers.Geometry.LineString(p.points);
// TBD Bounds only set for one of multiple geometries
}
}
// match Point
else if (OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
this.gmlns, "gml", "Point").length != 0) {
var point = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
this.gmlns, "gml", "Point")[0];
feature.fid = point.parentNode.parentNode.getAttribute('fid');
p = this.parseCoords(point);
if (p.points) {
geom = p.points[0];
// TBD Bounds only set for one of multiple geometries
}
}
feature.geometry = geom;
if (this.extractAttributes) {
feature.attributes = this.parseAttributes(xmlNode);
}
return feature;
},
/**
* Method: parseAttributes
* recursive function parse the attributes of a GML node.
* Searches for any child nodes which aren't geometries,
* and gets their value.
*
* Parameters:
* xmlNode - {<DOMElement>}
*/
parseAttributes: function(xmlNode) {
var nodes = xmlNode.childNodes;
var attributes = {};
for(var i = 0; i < nodes.length; i++) {
var name = nodes[i].nodeName;
var value = OpenLayers.Util.getXmlNodeValue(nodes[i]);
// Ignore Geometry attributes
// match ".//gml:pos|.//gml:posList|.//gml:coordinates"
if((name.search(":pos")!=-1)
||(name.search(":posList")!=-1)
||(name.search(":coordinates")!=-1)){
continue;
}
// Check for a leaf node
if((nodes[i].childNodes.length == 1 && nodes[i].childNodes[0].nodeName == "#text")
|| (nodes[i].childNodes.length == 0 && nodes[i].nodeName!="#text")) {
attributes[name] = value;
}
OpenLayers.Util.extend(attributes, this.parseAttributes(nodes[i]))
}
return attributes;
},
/**
* Method: parsePolygonNode
*
* Parameters:
* xmlNode - {XMLNode}
*
* Returns:
* {<OpenLayers.Geometry.Polygon>} polygon geometry
*/
parsePolygonNode: function(polygonNode) {
var linearRings = OpenLayers.Ajax.getElementsByTagNameNS(polygonNode,
this.gmlns, "gml", "LinearRing");
var rings = [];
var p;
var polyBounds;
for (var i = 0; i < linearRings.length; i++) {
p = this.parseCoords(linearRings[i]);
ring1 = new OpenLayers.Geometry.LinearRing(p.points);
rings.push(ring1);
}
var poly = new OpenLayers.Geometry.Polygon(rings);
return poly;
},
/**
* Method: parseCoords
* Extract Geographic coordinates from an XML node.
*
* Parameters:
* xmlNode - {<XMLNode>}
*
* Returns:
* An array of <OpenLayers.Geometry.Point> points.
*/
parseCoords: function(xmlNode) {
var x, y, left, bottom, right, top, bounds;
var p = []; // return value = [points,bounds]
if (xmlNode) {
p.points = [];
// match ".//gml:pos|.//gml:posList|.//gml:coordinates"
// Note: GML2 coordinates are of the form:x y,x y,x y
// GML2 can also be of the form <coord><x>1</x><y>2</y></coord>
// GML3 posList is of the form:x y x y. OR x y z x y z.
// GML3 Line or Polygon
var coordNodes = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, this.gmlns, "gml", "posList");
// GML3 Point
if (coordNodes.length == 0) {
coordNodes = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, this.gmlns, "gml", "pos");
}
// GML2
if (coordNodes.length == 0) {
coordNodes = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, this.gmlns, "gml", "coordinates");
}
// TBD: Need to handle an array of coordNodes not just coordNodes[0]
var coordString = OpenLayers.Util.getXmlNodeValue(coordNodes[0]);
// Extract an array of Numbers from CoordString
var nums = (coordString) ? coordString.split(/[, \n\t]+/) : [];
// Remove elements caused by leading and trailing white space
while (nums[0] == "")
nums.shift();
while (nums[nums.length-1] == "")
nums.pop();
for(i = 0; i < nums.length; i = i + this.dim) {
x = parseFloat(nums[i]);
y = parseFloat(nums[i+1]);
p.points.push(new OpenLayers.Geometry.Point(x, y));
}
}
return p;
},
/**
* APIMethod: write
* Accept Feature Array, and return a string.
*
* Parameters:
* features - Array({<OpenLayers.Feature.Vector>}> List of features to
* serialize into a string.
*/
write: function(features) {
var featureCollection = document.createElementNS("http://www.opengis.net/wfs", "wfs:" + this.collectionName);
for (var i=0; i < features.length; i++) {
featureCollection.appendChild(this.createFeatureXML(features[i]));
}
return featureCollection;
},
/**
* Method: createFeatureXML
* Accept an OpenLayers.Feature.Vector, and build a geometry for it.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
*
* Returns:
* {DOMElement}
*/
createFeatureXML: function(feature) {
var geometryNode = this.buildGeometryNode(feature.geometry);
var geomContainer = document.createElementNS(this.featureNS, "feature:"+this.geometryName);
geomContainer.appendChild(geometryNode);
var featureNode = document.createElementNS(this.gmlns, "gml:" + this.featureName);
var featureContainer = document.createElementNS(this.featureNS, "feature:"+this.layerName);
featureContainer.appendChild(geomContainer);
for(var attr in feature.attributes) {
var attrText = document.createTextNode(feature.attributes[attr]);
var nodename = attr;
if (attr.search(":") != -1) {
nodename = attr.split(":")[1];
}
var attrContainer = document.createElementNS(this.featureNS, "feature:"+nodename);
attrContainer.appendChild(attrText);
featureContainer.appendChild(attrContainer);
}
featureNode.appendChild(featureContainer);
return featureNode;
},
/**
* Method: buildGeometryNode
* builds a GML file with a given geometry
*
* Parameters:
* geometry - {<OpenLayers.Geometry>}
*/
buildGeometryNode: function(geometry) {
// TBD test if geoserver can be given a Multi-geometry for a simple-geometry data store
// ie if multipolygon can be sent for a polygon feature type
var gml = "";
// match MultiPolygon or Polygon
if (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon"
|| geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
gml = document.createElementNS(this.gmlns, 'gml:MultiPolygon');
// TBD retrieve the srs from layer
// srsName is non-standard, so not including it until it's right.
//gml.setAttribute("srsName", "http://www.opengis.net/gml/srs/epsg.xml#4326");
var polygonMember = document.createElementNS(this.gmlns, 'gml:polygonMember');
var polygon = document.createElementNS(this.gmlns, 'gml:Polygon');
var outerRing = document.createElementNS(this.gmlns, 'gml:outerBoundaryIs');
var linearRing = document.createElementNS(this.gmlns, 'gml:LinearRing');
// TBD manage polygons with holes
linearRing.appendChild(this.buildCoordinatesNode(geometry.components[0]));
outerRing.appendChild(linearRing);
polygon.appendChild(outerRing);
polygonMember.appendChild(polygon);
gml.appendChild(polygonMember);
}
// match MultiLineString or LineString
else if (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString"
|| geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") {
gml = document.createElementNS(this.gmlns, 'gml:MultiLineString');
// TBD retrieve the srs from layer
// srsName is non-standard, so not including it until it's right.
//gml.setAttribute("srsName", "http://www.opengis.net/gml/srs/epsg.xml#4326");
var lineStringMember = document.createElementNS(this.gmlns, 'gml:lineStringMember');
var lineString = document.createElementNS(this.gmlns, 'gml:LineString');
lineString.appendChild(this.buildCoordinatesNode(geometry));
lineStringMember.appendChild(lineString);
gml.appendChild(lineStringMember);
}
// match MultiPoint or Point
else if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point" ||
geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") {
// TBD retrieve the srs from layer
// srsName is non-standard, so not including it until it's right.
//gml.setAttribute("srsName", "http://www.opengis.net/gml/srs/epsg.xml#4326");
gml = document.createElementNS(this.gmlns, 'gml:MultiPoint');
var parts = "";
if (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") {
parts = geometry.components;
} else {
parts = [geometry];
}
for (var i = 0; i < parts.length; i++) {
var pointMember = document.createElementNS(this.gmlns, 'gml:pointMember');
var point = document.createElementNS(this.gmlns, 'gml:Point');
point.appendChild(this.buildCoordinatesNode(parts[i]));
pointMember.appendChild(point);
gml.appendChild(pointMember);
}
}
return gml;
},
/**
* Method: buildCoordinates
* builds the coordinates XmlNode
* <gml:coordinates decimal="." cs="," ts=" ">...</gml:coordinates>
*
* Parameters:
* geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {XmlNode} created xmlNode
*/
buildCoordinatesNode: function(geometry) {
var coordinatesNode = document.createElementNS(this.gmlns, "gml:coordinates");
coordinatesNode.setAttribute("decimal", ".");
coordinatesNode.setAttribute("cs", ",");
coordinatesNode.setAttribute("ts", " ");
var points = null;
if (geometry.components) {
points = geometry.components;
}
var path = "";
if (points) {
for (var i = 0; i < points.length; i++) {
path += points[i].x + "," + points[i].y + " ";
}
} else {
path += geometry.x + "," + geometry.y + " ";
}
var txtNode = document.createTextNode(path);
coordinatesNode.appendChild(txtNode);
return coordinatesNode;
},
/** @final @type String */
CLASS_NAME: "OpenLayers.Format.GML"
});