define(function () {
|
"use strict";
|
function JsonGMLParser(extractAttributes, xy, gmlnsm, wfsnsm, featureName, geometryAttribute) {
|
this._extractAttributes = extractAttributes;
|
this._featureName = featureName || "featureMember";
|
this._xy = xy;
|
this._gmlns = gmlnsm || "http://www.opengis.net/gml",
|
this._wfs = wfsnsm || "http://www.opengis.net/wfs",
|
this._geometryAttribute = geometryAttribute;
|
|
// compile regular expressions once instead of every time they are used
|
this._regExes = {
|
trimSpace: (/^\s*|\s*$/g),
|
removeSpace: (/\s*/g),
|
splitSpace: (/\s+/),
|
trimComma: (/\s*,\s*/g)
|
};
|
}
|
|
Object.defineProperties(JsonGMLParser.prototype, {
|
extractAttributes: {
|
get: function () {
|
return this._extractAttributes;
|
},
|
set: function (value) {
|
this._extractAttributes = value;
|
}
|
},
|
|
xy: {
|
get: function () {
|
return this._xy;
|
},
|
}
|
});
|
|
JsonGMLParser.prototype.read = function (data) {
|
if (this._wfsPrefix == undefined) {
|
this.detectPrefixes();
|
}
|
|
var documentElement = data["?xml"];
|
if (documentElement == undefined)
|
documentElement = data[this._wfsPrefix + "FeatureCollection"];
|
else
|
documentElement = data["?xml"][this._wfsPrefix + "FeatureCollection"];
|
|
if (documentElement == undefined) {
|
throw "Invalid GML format. Could not find root element";
|
}
|
|
var featureNodes = documentElement[this._gmlPrefix + this._featureName] || [];
|
var features = [];
|
var objName = this._featureName;
|
|
if (featureNodes.length == undefined) {
|
var arr = Object.keys(featureNodes).map(function (key) { return { objName: featureNodes[key] }; });
|
featureNodes = arr;
|
}
|
for (var i = 0; i < featureNodes.length; i++) {
|
var featureNode = featureNodes[i];
|
var typeName = Object.getOwnPropertyNames(featureNode)[0];
|
var feature = this.parseFeature(featureNode[typeName]);
|
if (feature) {
|
features.push(feature);
|
}
|
}
|
return features;
|
};
|
|
JsonGMLParser.prototype.detectPrefixes = function (data) {
|
this._wfsPrefix = "wfs:";
|
this._gmlPrefix = "gml:";
|
}
|
JsonGMLParser.prototype.detectGeometryAttribute = function (data) {
|
this._geometryAttribute = "Geom";
|
}
|
/**
|
* 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:
|
* node - {DOMElement} A GML feature node.
|
*/
|
|
function containGeometryAttribute(propName, geometryAttribute) {
|
|
var index = propName.indexOf(':' + geometryAttribute);
|
return (index > 0 && index == propName.length - geometryAttribute.length - 1);
|
}
|
|
JsonGMLParser.prototype.parseFeature = function (node) {
|
// only accept one geometry per feature - look for highest "order"
|
var order = ["MultiPolygon", "Polygon",
|
"MultiLineString", "LineString",
|
"MultiPoint", "Point"];
|
// FIXME: In case we parse a feature with no geometry, but boundedBy an Envelope,
|
// this code creates a geometry derived from the Envelope. This is not correct.
|
if (this._geometryAttribute == null)
|
this.detectGeometryAttribute(node);
|
var geometry, type, parser;
|
var attributes = {};
|
var nemes = "";
|
for (var propName in node) {
|
if (nemes == "") {
|
nemes = propName;
|
}
|
if (propName == this._geometryAttribute || containGeometryAttribute(propName, this._geometryAttribute)) {
|
var fullType = Object.getOwnPropertyNames(node[propName])[0]
|
type = fullType.replace(this._gmlPrefix, "");
|
parser = this.parseGeometry[type.toLowerCase()];
|
if (parser) {
|
geometry = parser.apply(this, [node[propName][fullType]]);
|
}
|
else {
|
console.log("unsupportedGeometryType:" + type);
|
}
|
}
|
else if (propName != "_attributes") {
|
attributes[propName] = node[propName].value;
|
}
|
}
|
|
// TODO: optinally parse gml:boundedBy on feature
|
var bounds;
|
|
// construct feature (optionally with attributes)
|
if (this.extractAttributes == false) {
|
attributes = undefined;
|
}
|
var feature =
|
{
|
geometryType: type.toLowerCase(),
|
positions: geometry,
|
attributes: attributes
|
};
|
|
feature.bounds = bounds;
|
|
// assign fid - this can come from a "fid" or "id" attribute
|
// console.log(node);
|
//feature.fid = node["_attributes"].fid || node["_attributes"].id;
|
if (node["_attributes"] != undefined) {
|
feature.fid = node["_attributes"].fid || node["_attributes"].id;
|
} else {
|
feature.fid = node[nemes].value;
|
}
|
return feature;
|
};
|
|
/**
|
* Property: parseGeometry
|
* Properties of this object are the functions that parse geometries based
|
* on their type.
|
*/
|
JsonGMLParser.prototype.parseGeometry = {
|
|
/**
|
* Method: parseGeometry.point
|
* Given a GML node representing a point geometry, create an OpenLayers
|
* point geometry.
|
*
|
* Parameters:
|
* node - {DOMElement} A GML node.
|
*
|
* Returns:
|
* {<OpenLayers_Geometry_Point>} A point geometry.
|
*/
|
point: function (node) {
|
/**
|
* Three coordinate variations to consider:
|
* 1) <gml:pos>x y z</gml:pos>
|
* 2) <gml:coordinates>x, y, z</gml:coordinates>
|
* 3) <gml:coord><gml:X>x</gml:X><gml:Y>y</gml:Y></gml:coord>
|
*/
|
var nodeList, coordString;
|
var coords = [];
|
|
// look for <gml:pos>
|
if (node[this._gmlPrefix + "pos"]) {
|
coordString = node[this._gmlPrefix + "pos"].value;
|
coordString = coordString.replace(this._regExes.trimSpace, "");
|
coords = coordString.split(this._regExes.splitSpace);
|
}
|
// look for <gml:coordinates>
|
else if (node[this._gmlPrefix + "coordinates"]) {
|
coordString = node[this._gmlPrefix + "coordinates"].value;
|
coordString = coordString.replace(this._regExes.removeSpace, "");
|
coords = coordString.split(",");
|
}
|
|
// look for <gml:coord>
|
else if (node[this._gmlPrefix + "coord"]) {
|
var xVal = node[this._gmlPrefix + "coord"][this._gmlPrefix + "X"].value;
|
var yVal = node[this._gmlPrefix + "coord"][this._gmlPrefix + "Y"].value;
|
if (xVal != undefined && yVal != undefined) {
|
coords = [xVal, yVal];
|
}
|
}
|
|
// preserve third dimension
|
if (coords.length == 2) {
|
coords[2] = null;
|
}
|
|
if (this.xy) {
|
return [parseFloat(coords[0]), parseFloat(coords[1]), parseFloat(coords[2])];
|
}
|
else {
|
return [parseFloat(coords[1]), parseFloat(coords[0]), parseFloat(coords[2])];
|
}
|
},
|
|
/**
|
* Method: parseGeometry.multipoint
|
* Given a GML node representing a multipoint geometry, create an
|
* OpenLayers multipoint geometry.
|
*
|
* Parameters:
|
* node - {DOMElement} A GML node.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
|
*/
|
multipoint: function (node) {
|
var nodeList = node[this._gmlPrefix + "pointMember"];
|
var components = [];
|
|
if (nodeList.length == undefined)
|
nodeList = [nodeList];
|
|
if (nodeList.length > 0) {
|
var point;
|
for (var i = 0; i < nodeList.length; ++i) {
|
point = this.parseGeometry.point.apply(this, [nodeList[i][this._gmlPrefix + "Point"]]);
|
if (point) {
|
components.push(point);
|
}
|
}
|
}
|
return components;
|
},
|
|
/**
|
* Method: parseGeometry.linestring
|
* Given a GML node representing a linestring geometry, create an
|
* OpenLayers linestring geometry.
|
*
|
* Parameters:
|
* node - {DOMElement} A GML node.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.LineString>} A linestring geometry.
|
*/
|
linestring: function (node) {
|
/**
|
* Two coordinate variations to consider:
|
* 1) <gml:posList dimension="d">x0 y0 z0 x1 y1 z1</gml:posList>
|
* 2) <gml:coordinates>x0, y0, z0 x1, y1, z1</gml:coordinates>
|
*/
|
var nodeList, coordString;
|
var coords = [];
|
var points = [];
|
|
// look for <gml:posList>
|
if (node[this._gmlPrefix + "posList"]) {
|
coordString = node[this._gmlPrefix + "posList"].value;
|
coordString = coordString.replace(this._regExes.trimSpace, "");
|
coords = coordString.split(this._regExes.splitSpace);
|
var dim = node[this._gmlPrefix + "posList"]._attributes["dimension"];
|
var j, x, y, z;
|
for (var i = 0; i < coords.length / dim; ++i) {
|
j = i * dim;
|
x = parseFloat(coords[j]);
|
y = parseFloat(coords[j + 1]);
|
z = (dim == 2) ? null : parseFloat(coords[j + 2]);
|
if (this.xy) {
|
points.push(x, y, z);
|
} else {
|
points.push(y, x, z);
|
}
|
}
|
}
|
|
// look for <gml:coordinates>
|
else if (node[this._gmlPrefix + "coordinates"]) {
|
coordString = node[this._gmlPrefix + "coordinates"].value;
|
coordString = coordString.replace(this._regExes.trimSpace, "");
|
coordString = coordString.replace(this._regExes.trimComma, ",");
|
var pointList = coordString.split(this._regExes.splitSpace);
|
for (var i = 0; i < pointList.length; ++i) {
|
coords = pointList[i].split(",");
|
if (coords.length == 2) {
|
coords[2] = null;
|
}
|
if (this.xy) {
|
points.push(parseFloat(coords[0]),
|
parseFloat(coords[1]),
|
parseFloat(coords[2]));
|
} else {
|
points.push(parseFloat(coords[1]),
|
parseFloat(coords[0]),
|
parseFloat(coords[2]));
|
}
|
}
|
}
|
|
return points;
|
},
|
|
/**
|
* Method: parseGeometry.multilinestring
|
* Given a GML node representing a multilinestring geometry, create an
|
* OpenLayers multilinestring geometry.
|
*
|
* Parameters:
|
* node - {DOMElement} A GML node.
|
*
|
* Returns:
|
* {<OpenLayers.Geometry.MultiLineString>} A multilinestring geometry.
|
*/
|
multilinestring: function (node) {
|
var nodeList = node[this._gmlPrefix + "lineStringMember"];
|
var components = [];
|
|
if (nodeList.length == undefined)
|
nodeList = [nodeList];
|
|
if (nodeList.length > 0) {
|
var line;
|
for (var i = 0; i < nodeList.length; ++i) {
|
line = this.parseGeometry.linestring.apply(this, [nodeList[i][this._gmlPrefix + "LineString"]]);
|
if (line) {
|
components.push(line);
|
}
|
}
|
}
|
return components;
|
},
|
|
/**
|
* Method: parseGeometry.polygon
|
* Given a GML node representing a polygon geometry, create an
|
* OpenLayers polygon geometry.
|
*
|
* Parameters:
|
* node - {DOMElement} A GML node.
|
*
|
* Returns:
|
* {<OpenLayers_Geometry_Polygon>} A polygon geometry.
|
*/
|
polygon: function (node) {
|
var rings = [node[this._gmlPrefix + "outerBoundaryIs"][this._gmlPrefix + "LinearRing"]];
|
if (node[this._gmlPrefix + "innerBoundaryIs"]) {
|
for (var i = 0; i < node[this._gmlPrefix + "innerBoundaryIs"].length; i++)
|
rings.push(node[this._gmlPrefix + "innerBoundaryIs"][i][this._gmlPrefix + "LinearRing"]);
|
}
|
var components = [];
|
if (rings.length > 0) {
|
// this assumes exterior ring first, inner rings after
|
var ring;
|
for (var i = 0; i < rings.length; ++i) {
|
ring = this.parseGeometry.linestring.apply(this, [rings[i]]);
|
if (ring) {
|
components.push(ring);
|
}
|
}
|
}
|
return components;
|
},
|
|
/**
|
* Method: parseGeometry.multipolygon
|
* Given a GML node representing a multipolygon geometry, create an
|
* OpenLayers multipolygon geometry.
|
*
|
* Parameters:
|
* node - {DOMElement} A GML node.
|
*
|
* Returns:
|
* {<OpenLayers_Geometry_MultiPolygon>} A multipolygon geometry.
|
*/
|
multipolygon: function (node) {
|
var nodeList = node[this._gmlPrefix + "polygonMember"];
|
var components = [];
|
|
if (nodeList.length == undefined)
|
nodeList = [nodeList];
|
|
if (nodeList.length > 0) {
|
var polygon;
|
for (var i = 0; i < nodeList.length; ++i) {
|
polygon = this.parseGeometry.polygon.apply(this, [nodeList[i][this._gmlPrefix + "Polygon"]]);
|
if (polygon) {
|
components.push(polygon);
|
}
|
}
|
}
|
return components;
|
},
|
};
|
|
return JsonGMLParser;
|
});
|