diff --git a/package.json b/package.json
index d0adf45e65..4b53c30fbb 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
"istanbul-instrumenter-loader": "2.0.0",
"jsdoc": "3.4.3",
"jsdoc-jsx": "0.1.0",
+ "json-loader": "0.5.4",
"karma": "1.5.0",
"karma-chrome-launcher": "2.0.0",
"karma-cli": "1.0.1",
diff --git a/web/client/test-resources/wfs/describe-pois.json b/web/client/test-resources/wfs/describe-pois.json
new file mode 100644
index 0000000000..bf904fcfb9
--- /dev/null
+++ b/web/client/test-resources/wfs/describe-pois.json
@@ -0,0 +1,44 @@
+{
+ "elementFormDefault":"qualified",
+ "targetNamespace":"http://www.census.gov",
+ "targetPrefix":"tiger",
+ "featureTypes":[
+ {
+ "typeName":"poi",
+ "properties":[
+ {
+ "name":"the_geom",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"gml:Point",
+ "localType":"Point"
+ },
+ {
+ "name":"NAME",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:string",
+ "localType":"string"
+ },
+ {
+ "name":"THUMBNAIL",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:string",
+ "localType":"string"
+ },
+ {
+ "name":"MAINPAGE",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:string",
+ "localType":"string"
+ }
+ ]
+ }
+ ]
+}
diff --git a/web/client/test-resources/wfs/describe-states.json b/web/client/test-resources/wfs/describe-states.json
new file mode 100644
index 0000000000..36254d7d89
--- /dev/null
+++ b/web/client/test-resources/wfs/describe-states.json
@@ -0,0 +1,196 @@
+{
+ "elementFormDefault":"qualified",
+ "targetNamespace":"http://www.openplans.org/topp",
+ "targetPrefix":"topp",
+ "featureTypes":[
+ {
+ "typeName":"states",
+ "properties":[
+ {
+ "name":"the_geom",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"gml:MultiPolygon",
+ "localType":"MultiPolygon"
+ },
+ {
+ "name":"STATE_NAME",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:string",
+ "localType":"string"
+ },
+ {
+ "name":"STATE_FIPS",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:string",
+ "localType":"string"
+ },
+ {
+ "name":"SUB_REGION",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:string",
+ "localType":"string"
+ },
+ {
+ "name":"STATE_ABBR",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:string",
+ "localType":"string"
+ },
+ {
+ "name":"LAND_KM",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"WATER_KM",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"PERSONS",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"FAMILIES",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"HOUSHOLD",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"MALE",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"FEMALE",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"WORKERS",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"DRVALONE",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"CARPOOL",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"PUBTRANS",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"EMPLOYED",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"UNEMPLOY",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"SERVICE",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"MANUAL",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"P_MALE",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"P_FEMALE",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ },
+ {
+ "name":"SAMP_POP",
+ "maxOccurs":1,
+ "minOccurs":0,
+ "nillable":true,
+ "type":"xsd:number",
+ "localType":"number"
+ }
+ ]
+ }
+ ]
+}
diff --git a/web/client/test-resources/wfs/museam.json b/web/client/test-resources/wfs/museam.json
new file mode 100644
index 0000000000..97e7d5c7a8
--- /dev/null
+++ b/web/client/test-resources/wfs/museam.json
@@ -0,0 +1,29 @@
+{
+ "type":"FeatureCollection",
+ "totalFeatures":6,
+ "features":[
+ {
+ "type":"Feature",
+ "id":"poi.1",
+ "geometry":{
+ "type":"Point",
+ "coordinates":[
+ -74.0104611,
+ 40.70758763
+ ]
+ },
+ "geometry_name":"the_geom",
+ "properties":{
+ "NAME":"museam",
+ "THUMBNAIL":"pics/22037827-Ti.jpg",
+ "MAINPAGE":"pics/22037827-L.jpg"
+ }
+ }
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"urn:ogc:def:crs:EPSG::4326"
+ }
+ }
+}
diff --git a/web/client/test-resources/wfst/insert/Wyoming_1_1_0.xml b/web/client/test-resources/wfst/insert/Wyoming_1_1_0.xml
new file mode 100644
index 0000000000..7344df0649
--- /dev/null
+++ b/web/client/test-resources/wfst/insert/Wyoming_1_1_0.xml
@@ -0,0 +1 @@
+Wyoming56MtnWY251500.8011848.1494535881198251688392270072265811645611536792810929632078681311271419291570.50.583202-104.053108 41.698246 -104.054993 41.564247 -104.053505 41.388107 -104.051201 41.003227 -104.933968 40.994305 -105.278259 40.996365 -106.202896 41.000111 -106.328545 41.001316 -106.864838 40.998489 -107.303436 41.000168 -107.918037 41.00341 -109.047638 40.998474 -110.001457 40.997646 -110.062477 40.99794 -111.050285 40.996635 -111.050911 41.25848 -111.050323 41.578648 -111.047951 41.996265 -111.046028 42.503323 -111.048447 43.019962 -111.04673 43.284813 -111.045998 43.515606 -111.049629 43.982632 -111.050789 44.473396 -111.050842 44.664562 -111.05265 44.995766 -110.428894 44.992348 -110.392006 44.998688 -109.994789 45.002853 -109.798653 44.99958 -108.624573 44.997643 -108.258568 45.00016 -107.893715 44.999813 -106.258644 44.996174 -106.020576 44.997227 -105.084465 44.999832 -105.04126 45.001091 -104.059349 44.997349 -104.058975 44.574368 -104.060547 44.181843 -104.059242 44.145844 -104.05899 43.852928 -104.057426 43.503738 -104.05867 43.47916 -104.05571 43.003094 -104.055725 42.614704 -104.053009 41.999851 -104.053108 41.698246
diff --git a/web/client/test-resources/wfst/insert/museam_1_1_0.xml b/web/client/test-resources/wfst/insert/museam_1_1_0.xml
new file mode 100644
index 0000000000..7bde341869
--- /dev/null
+++ b/web/client/test-resources/wfst/insert/museam_1_1_0.xml
@@ -0,0 +1 @@
+museampics/22037827-Ti.jpgpics/22037827-L.jpg-74.0104611 40.70758763
diff --git a/web/client/utils/FilterUtils.jsx b/web/client/utils/FilterUtils.jsx
index a6f33dea16..f8e9461caf 100644
--- a/web/client/utils/FilterUtils.jsx
+++ b/web/client/utils/FilterUtils.jsx
@@ -6,9 +6,7 @@
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
-
-const {isArray} = require('lodash');
-
+const {processOGCGeometry, closePolygon, pointElement, polygonElement, lineStringElement } = require("./ogc/GML");
const normalizeVersion = (version) => {
if (!version) {
return "2.0";
@@ -359,124 +357,11 @@ const FilterUtils = {
}
return filter;
},
- closePolygon: function(coords) {
- if (coords.length >= 3) {
- const first = coords[0];
- const last = coords[coords.length - 1];
- if ((first[0] !== last[0]) || (first[1] !== last[1])) {
- return coords.concat([coords[0]]);
- }
- }
- return coords;
- },
- getGmlPolygonElement: function(coordinates, srsName, version) {
- let gmlPolygon = '' : '>';
-
- // ///////////////////////////////////////////////////////////////////////////////////////////////////////
- // Array of LinearRing coordinate array. The first element in the array represents the exterior ring.
- // Any subsequent elements represent interior rings (or holes).
- // ///////////////////////////////////////////////////////////////////////////////////////////////////////
-
- const normalizedCoords = coordinates.length && isArray(coordinates[0]) && coordinates[0].length && isArray(coordinates[0][0]) ? coordinates : [coordinates];
- normalizedCoords.forEach((element, index) => {
- let coords = this.closePolygon(element).map((coordinate) => {
- return coordinate[0] + (version === "1.0.0" ? "," : " ") + coordinate[1];
- });
- const exterior = (version === "1.0.0" ? "outerBoundaryIs" : "exterior");
- const interior = (version === "1.0.0" ? "innerBoundaryIs" : "exterior");
- gmlPolygon +=
- (index < 1 ? '' : '') +
- '' +
- (version === "1.0.0" ? '' : '') +
- coords.join(" ") +
- (version === "1.0.0" ? '' : '') +
- '' +
- (index < 1 ? '' : '');
- });
-
- gmlPolygon += '';
- return gmlPolygon;
- },
- getGmlLineStringElement: function(coordinates, srsName, version) {
- let gml = '' : '>';
-
- // ///////////////////////////////////////////////////////////////////////////////////////////////////////
- // Array of LinearRing coordinate array. The first element in the array represents the exterior ring.
- // Any subsequent elements represent interior rings (or holes).
- // ///////////////////////////////////////////////////////////////////////////////////////////////////////
- coordinates.forEach((element) => {
- let coords = element.map((coordinate) => {
- return coordinate[0] + (version === "1.0.0" ? "," : " ") + coordinate[1];
- });
- gml += (version === "1.0.0" ? '' : '') +
- coords.join(" ") +
- (version === "1.0.0" ? '' : '');
- });
-
- gml += '';
- return gml;
- },
- processOGCGeometry: function(version, geometry) {
- let ogc = '';
- switch (geometry.type) {
- case "Point":
- ogc += this.getGmlPointElement(geometry.coordinates,
- geometry.projection || "EPSG:4326", version);
- break;
- case "MultiPoint":
- ogc += '';
-
- // //////////////////////////////////////////////////////////////////////////
- // Coordinates of a MultiPoint are an array of positions
- // //////////////////////////////////////////////////////////////////////////
- geometry.coordinates.forEach((element) => {
- let point = element;
- if (point) {
- ogc += "";
- ogc += this.getGmlPointElement(point, version);
- ogc += "";
- }
- });
-
- ogc += '';
- break;
- case "LineString":
- ogc += this.getGmlLineStringElement(geometry.coordinates,
- geometry.projection || "EPSG:4326", version);
- break;
- case "Polygon":
- ogc += this.getGmlPolygonElement(geometry.coordinates,
- geometry.projection || "EPSG:4326", version);
- break;
- case "MultiPolygon":
- const multyPolygonTagName = version === "2.0" ? "MultiSurface" : "MultiPolygon";
- const polygonMemberTagName = version === "2.0" ? "surfaceMembers" : "polygonMember";
-
- ogc += '';
-
- // //////////////////////////////////////////////////////////////////////////
- // Coordinates of a MultiPolygon are an array of Polygon coordinate arrays
- // //////////////////////////////////////////////////////////////////////////
- geometry.coordinates.forEach((element) => {
- let polygon = element;
- if (polygon) {
- ogc += "";
- ogc += this.getGmlPolygonElement(polygon, version);
- ogc += "";
- }
- });
-
- ogc += '';
- break;
- default:
- break;
- }
- return ogc;
- },
+ closePolygon: closePolygon,
+ getGmlPointElement: pointElement,
+ getGmlPolygonElement: polygonElement,
+ getGmlLineStringElement: lineStringElement,
+ processOGCGeometry,
processOGCSpatialFilter: function(version, objFilter, nsplaceholder) {
let ogc = ogcSpatialOperator[objFilter.spatialField.operation].startTag;
ogc +=
@@ -594,29 +479,6 @@ const FilterUtils = {
* }}
* }
*/
- getGmlPointElement: function(coordinates, srsName, version) {
- let gmlPoint = '' : '>';
- const normalizedCoords = coordinates.length && isArray(coordinates[0]) ? coordinates : [coordinates];
- // ///////////////////////////////////////////////////////////////////////////////////////////////////////
- // Array of LinearRing coordinate array. The first element in the array represents the exterior ring.
- // Any subsequent elements represent interior rings (or holes).
- // ///////////////////////////////////////////////////////////////////////////////////////////////////////
- normalizedCoords.forEach((element) => {
- let coords = element.map((coordinate) => {
- return coordinate[0] + " " + coordinate[1];
- });
- if (version === "1.0.0") {
- gmlPoint += '' + element[0][0] + '' + element[0][1] + '';
- } else {
- gmlPoint += '' + coords.join(" ") + '';
- }
- });
-
- gmlPoint += '';
- return gmlPoint;
- },
processOGCCrossLayerFilter: function(crossLayerFilter, nsplaceholderparams) {
let ogc = ogcSpatialOperator[crossLayerFilter.operation].startTag;
let nsplaceholder = nsplaceholderparams || "ogc";
diff --git a/web/client/utils/__tests__/FilterUtils-test.js b/web/client/utils/__tests__/FilterUtils-test.js
index fb230b79f2..9ebea05edf 100644
--- a/web/client/utils/__tests__/FilterUtils-test.js
+++ b/web/client/utils/__tests__/FilterUtils-test.js
@@ -332,7 +332,7 @@ describe('FilterUtils', () => {
"geometry": {
"type": "Point",
"projection": "EPSG:4326",
- "coordinates": [[[1, 1]]]
+ "coordinates": [1, 1]
}
}
};
@@ -348,7 +348,7 @@ describe('FilterUtils', () => {
"geometry": {
"type": "Point",
"projection": "EPSG:4326",
- "coordinates": [[[1, 1]]]
+ "coordinates": [1, 1]
}
}
};
@@ -380,7 +380,7 @@ describe('FilterUtils', () => {
"geometry": {
"type": "Point",
"projection": "EPSG:4326",
- "coordinates": [[[1, 1]]]
+ "coordinates": [1, 1]
}
}
};
@@ -396,7 +396,7 @@ describe('FilterUtils', () => {
"geometry": {
"type": "Point",
"projection": "EPSG:4326",
- "coordinates": [[[1, 1]]]
+ "coordinates": [1, 1]
}
}
};
@@ -428,7 +428,7 @@ describe('FilterUtils', () => {
"geometry": {
"type": "Point",
"projection": "EPSG:4326",
- "coordinates": [[[1, 1]]]
+ "coordinates": [1, 1]
}
}
};
diff --git a/web/client/utils/ogc/GML/index.js b/web/client/utils/ogc/GML/index.js
new file mode 100644
index 0000000000..9056a37b76
--- /dev/null
+++ b/web/client/utils/ogc/GML/index.js
@@ -0,0 +1,168 @@
+const {isArray} = require('lodash');
+const isGML1 = (version) => version === "1.0.0";
+const closePolygon = (coords) => {
+ if (coords.length >= 3) {
+ const first = coords[0];
+ const last = coords[coords.length - 1];
+ if ((first[0] !== last[0]) || (first[1] !== last[1])) {
+ return coords.concat([coords[0]]);
+ }
+ }
+ return coords;
+};
+const pointElement = (coordinates, srsName, version) => {
+ let gmlPoint = '' : '>';
+ if (gml1) {
+ gmlPoint += '' + coordinates[0] + '' + coordinates[1] + '';
+ } else {
+ gmlPoint += '' + coordinates.join(" ") + '';
+ }
+
+
+ gmlPoint += '';
+ return gmlPoint;
+};
+
+const polygonElement = (coordinates, srsName, version) => {
+ const gml1 = isGML1(version);
+ let gmlPolygon = '' : '>';
+
+ // ///////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Array of LinearRing coordinate array. The first element in the array represents the exterior ring.
+ // Any subsequent elements represent interior rings (or holes).
+ // ///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ const normalizedCoords = coordinates.length && isArray(coordinates[0]) && coordinates[0].length && isArray(coordinates[0][0]) ? coordinates : [coordinates];
+ normalizedCoords.forEach((element, index) => {
+ let coords = closePolygon(element).map((coordinate) => {
+ return coordinate[0] + (gml1 ? "," : " ") + coordinate[1];
+ });
+ const exterior = (gml1 ? "outerBoundaryIs" : "exterior");
+ const interior = (gml1 ? "innerBoundaryIs" : "exterior");
+ gmlPolygon +=
+ (index < 1 ? '' : '') +
+ '' +
+ (gml1 ? '' : '') +
+ coords.join(" ") +
+ (gml1 ? '' : '') +
+ '' +
+ (index < 1 ? '' : '');
+ });
+
+ gmlPolygon += '';
+ return gmlPolygon;
+};
+const lineStringElement = (coordinates, srsName, version) => {
+ const gml1 = isGML1(version);
+ let gml = '' : '>';
+
+ // ///////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Array of LinearRing coordinate array. The first element in the array represents the exterior ring.
+ // Any subsequent elements represent interior rings (or holes).
+ // ///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ let coords = coordinates.map((coordinate) => {
+ return coordinate[0] + (gml1 ? "," : " ") + coordinate[1];
+ });
+ gml += (gml1 ? '' : '') +
+ coords.join(" ") +
+ (gml1 ? '' : '');
+
+ gml += '';
+ return gml;
+};
+
+
+/**
+ * Processes the geometry in geojson format to provide the GML version of it
+ * @param {string} version GML version
+ * @param {object} geometry the geometry in GeoJSON format
+ * @return {string} the GML version of the Geometry
+ */
+const processOGCGeometry = (version, geometry) => {
+ let ogc = '';
+ const srsName = geometry.projection || "EPSG:4326";
+ switch (geometry.type) {
+ case "Point":
+ ogc += pointElement(geometry.coordinates,
+ srsName, version);
+ break;
+ case "MultiPoint":
+ ogc += '';
+
+ // //////////////////////////////////////////////////////////////////////////
+ // Coordinates of a MultiPoint are an array of positions
+ // //////////////////////////////////////////////////////////////////////////
+ geometry.coordinates.forEach((element) => {
+ let point = element;
+ if (point) {
+ ogc += "";
+ ogc += pointElement(point, version);
+ ogc += "";
+ }
+ });
+
+ ogc += '';
+ break;
+ case "LineString":
+ ogc += lineStringElement(geometry.coordinates,
+ srsName, version);
+ break;
+ case "MultiLineString":
+ const multyLineTagName = version === "2.0" ? "MultiCurve" : "MultiLineString";
+ const lineMemberTagName = version === "2.0" ? "curveMember" : "lineStringMember";
+
+ ogc += ``;
+
+ // //////////////////////////////////////////////////////////////////////////
+ // Coordinates of a MultiPolygon are an array of Polygon coordinate arrays
+ // //////////////////////////////////////////////////////////////////////////
+ geometry.coordinates.forEach((element) => {
+ if (element) {
+ ogc += "";
+ ogc += lineStringElement(element, srsName, version);
+ ogc += "";
+ }
+ });
+ ogc += '';
+ break;
+ case "Polygon":
+ ogc += polygonElement(geometry.coordinates,
+ srsName, version);
+ break;
+ case "MultiPolygon":
+ const multyPolygonTagName = version === "2.0" ? "MultiSurface" : "MultiPolygon";
+ const polygonMemberTagName = version === "2.0" ? "surfaceMembers" : "polygonMember";
+
+ ogc += ``;
+
+ // //////////////////////////////////////////////////////////////////////////
+ // Coordinates of a MultiPolygon are an array of Polygon coordinate arrays
+ // //////////////////////////////////////////////////////////////////////////
+ geometry.coordinates.forEach((element) => {
+ let polygon = element;
+ if (polygon) {
+ ogc += "";
+ ogc += polygonElement(polygon, srsName, version);
+ ogc += "";
+ }
+ });
+ ogc += '';
+ break;
+ default:
+ break;
+ }
+ return ogc;
+};
+
+module.exports = {
+ closePolygon,
+ pointElement,
+ polygonElement,
+ lineStringElement,
+ processOGCGeometry
+};
diff --git a/web/client/utils/ogc/WFS/base.js b/web/client/utils/ogc/WFS/base.js
new file mode 100644
index 0000000000..248b52abf6
--- /dev/null
+++ b/web/client/utils/ogc/WFS/base.js
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017, GeoSolutions Sas.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+const {head, get} = require('lodash');
+const {processOGCGeometry} = require("../GML");
+/**
+ * Retrives the descriptor for a property in the describeFeatureType (supports single featureTypes)
+ * @memberof utils.ogc.WFS
+ * @param {string} propName the name of the property to get
+ * @param {object} describeFeatureType the describeFeatureType object
+ * @return {object} the property descriptor
+ */
+const getPropertyDesciptor = (propName, describeFeatureType) =>
+ head(
+ get(describeFeatureType, "featureTypes[0].properties").filter(d => d.name === propName)
+ );
+/**
+ * @name schemaLocation
+ * @memberof utils.ogc.WFS
+ * @param {object} describeFeatureType schemaLocation
+ * @return {string} url of the schemaLocation
+ */
+const schemaLocation = (d) => d.targetNamespace;
+
+/**
+ * Base utilities for WFS.
+ * @name WFS
+ * @memberof utils.ogc
+ */
+module.exports = {
+ schemaLocation,
+ /**
+ * retrieves the featureTypeSchema entry for XML from describeFeatureType
+ * @param {object} d describeFeatureType
+ * @return {string} the attribute. e.g. xmlns:topp="http://example.com/topp"
+ */
+ featureTypeSchema: (d) => `xmlns:${d.targetPrefix}="${schemaLocation(d)}"`,
+ /**
+ * Retrieve the value of the feature in GeoJSON to output the Geometry
+ * @param {object|number|string} value the value
+ * @param {string} key the attribute name
+ * @param {object} describeFeatureType the describeFeatureType object
+ * @return {string} the attribute value or a gml geometry
+ */
+ getValue: (value, key, describeFeatureType, version = "1.1.0") => {
+ // TODO implement normal attributes;
+ const isGeometryType = getPropertyDesciptor(key, describeFeatureType).type.indexOf("gml:") === 0;
+ if (isGeometryType) {
+ return processOGCGeometry(version, {
+ type: value.type,
+ coordinates: value.coordinates
+ });
+ }
+ return value;
+ },
+ getPropertyDesciptor,
+ /**
+ * retrives the featureTypeName from the describeFeatureType json object.
+ * It prepends the targetPrefix to the first typename found in the featureTypes array.
+ * Doesn't support multiple feature types
+ * @param {object} describeFeatureType the json object for describeFeatureType
+ * @return {string} the prefixed typenName
+ * @example
+ * getTypeName({targetPrefix: "topp",featureTypes: [{typeName: "states"}]); // --> topp:states
+ */
+ getTypeName: (dft) => dft.targetPrefix ? dft.targetPrefix + ":" + dft.featureTypes[0].typeName : dft.featureTypes[0].typeName,
+ processOGCGeometry
+};
diff --git a/web/client/utils/ogc/WFS.js b/web/client/utils/ogc/WFS/index.js
similarity index 98%
rename from web/client/utils/ogc/WFS.js
rename to web/client/utils/ogc/WFS/index.js
index e794b385b6..506b9fa977 100644
--- a/web/client/utils/ogc/WFS.js
+++ b/web/client/utils/ogc/WFS/index.js
@@ -39,7 +39,8 @@ const context = new Jsonix.Context([
/*eslint-enable */
const marshaller = context.createMarshaller();
const unmarshaller = context.createUnmarshaller();
-const WFS = {};
+const WFS = {
+};
module.exports = {WFS, marshaller, unmarshaller};
diff --git a/web/client/utils/ogc/WFST/index.js b/web/client/utils/ogc/WFST/index.js
new file mode 100644
index 0000000000..35afbbc7dd
--- /dev/null
+++ b/web/client/utils/ogc/WFST/index.js
@@ -0,0 +1 @@
+module.exports = require("./v1_1_0");
diff --git a/web/client/utils/ogc/WFST/v1_1_0/index.js b/web/client/utils/ogc/WFST/v1_1_0/index.js
new file mode 100644
index 0000000000..a58fe1e69d
--- /dev/null
+++ b/web/client/utils/ogc/WFST/v1_1_0/index.js
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2017, GeoSolutions Sas.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+const {transaction} = require('./transaction');
+const {insert} = require('./insert');
+
+module.exports = {
+ insert,
+ transaction
+};
diff --git a/web/client/utils/ogc/WFST/v1_1_0/insert.js b/web/client/utils/ogc/WFST/v1_1_0/insert.js
new file mode 100644
index 0000000000..4dc2db2420
--- /dev/null
+++ b/web/client/utils/ogc/WFST/v1_1_0/insert.js
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017, GeoSolutions Sas.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+const getAttributeName = (k, d) => d.targetPrefix ? d.targetPrefix + ":" + k : k;
+const {getValue, getTypeName, getPropertyDesciptor} = require("../../WFS/base");
+const attribute = (key, value) => `<${key}>${value}${key}>`;
+const attributes = (f, describeFeatureType) =>
+ Object.keys(f.properties)
+ .filter(k => getPropertyDesciptor(k, describeFeatureType))
+ .map((key) => attribute(getAttributeName(key, describeFeatureType), getValue(f.properties[key], key, describeFeatureType)));
+const geometryAttribute = (f, describeFeatureType) =>
+ attribute(getAttributeName(f.geometry_name, describeFeatureType), getValue(f.geometry, f.geometry_name, describeFeatureType));
+
+const feature = (f, describeFeatureType) => `<${getTypeName(describeFeatureType)}>`
+ + (attributes(f, describeFeatureType)
+ .concat(geometryAttribute(f, describeFeatureType))
+ ).join("")
+ + `${getTypeName(describeFeatureType)}>`;
+const features = (fs, describeFeatureType) => fs.map(f => feature(f, describeFeatureType)).join("");
+
+const insert = (fs, describeFeatureType) => ''
+ + `${features(fs.features, describeFeatureType)}`
+ + '';
+
+module.exports = {
+ insert,
+ feature
+};
diff --git a/web/client/utils/ogc/WFST/v1_1_0/transaction.js b/web/client/utils/ogc/WFST/v1_1_0/transaction.js
new file mode 100644
index 0000000000..6b767ed6cb
--- /dev/null
+++ b/web/client/utils/ogc/WFST/v1_1_0/transaction.js
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017, GeoSolutions Sas.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+
+const transaction = (operations, schemaLocation) => '`
+ + `${operations.map((o)=> o)}`
+ + '';
+module.exports = {
+ transaction
+};
diff --git a/web/client/utils/ogc/__tests__/GML-test.js b/web/client/utils/ogc/__tests__/GML-test.js
new file mode 100644
index 0000000000..acd3c25e1d
--- /dev/null
+++ b/web/client/utils/ogc/__tests__/GML-test.js
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2017, GeoSolutions Sas.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+const expect = require('expect');
+const {processOGCGeometry} = require('../GML');
+const V1_1_0 = "1.1.0";
+const point = {"type": "Point", "coordinates": [100.0, 0.0] };
+const lineString = { "type": "LineString",
+ "coordinates": [ [100.0, 0.0], [101.0, 1.0] ]
+};
+const polygon = {
+ "type": "Polygon",
+ "coordinates": [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]]
+};
+const multiPolygon = {
+ "type": "MultiPolygon",
+ "coordinates": [
+ [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]],
+ [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]],
+ [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]]
+ ]
+};
+const multiLineString = { "type": "MultiLineString",
+ "coordinates": [
+ [ [100.0, 0.0], [101.0, 1.0] ],
+ [ [102.0, 2.0], [103.0, 3.0] ]
+ ]
+ };
+const EXPECTED_RESULTS = {
+ V1_1_0: {
+ point: '100 0',
+ multiLineString: ''
+ + ''
+ + ''
+ + '100 0 101 1'
+ + ''
+ + ''
+ + ''
+ + ''
+ + '102 2 103 3'
+ + ''
+ + ''
+ + '',
+ lineString: ''
+ + '100 0 101 1'
+ + '',
+ polygon: ''
+ + ''
+ + ''
+ + '102 2 103 2 103 3 102 3 102 2'
+ + ''
+ + ''
+ + '',
+ multiPolygon: ''
+ + ''
+ + ''
+ + ''
+ + ''
+ + '102 2 103 2 103 3 102 3 102 2'
+ + ''
+ + ''
+ + ''
+ + ''
+ + ''
+ + ''
+ + ''
+ + ''
+ + '100 0 101 0 101 1 100 1 100 0'
+ + ''
+ + ''
+ + ''
+ + ''
+ + '100.2 0.2 100.8 0.2 100.8 0.8 100.2 0.8 100.2 0.2'
+ + ''
+ + ''
+ + ''
+ + ''
+ + ''
+ }
+};
+describe('Test GeoJSON/GML geometry conversion', () => {
+ it('Point to GML 1.1.0', () => {
+ const gmlPoint = processOGCGeometry(V1_1_0, point);
+ expect(gmlPoint).toEqual(EXPECTED_RESULTS.V1_1_0.point);
+ });
+
+ it('LineString to GML 1.1.0', () => {
+ const gmlLineString = processOGCGeometry(V1_1_0, lineString);
+ expect(gmlLineString).toBe(EXPECTED_RESULTS.V1_1_0.lineString);
+ });
+ it('MultiLineString to GML 1.1.0', () => {
+ const gmlLineString = processOGCGeometry(V1_1_0, multiLineString);
+ expect(gmlLineString).toBe(EXPECTED_RESULTS.V1_1_0.multiLineString);
+ });
+ it('Polygon to GML 1.1.0', () => {
+ const gmlPolygon = processOGCGeometry(V1_1_0, polygon);
+ expect(gmlPolygon).toBe(EXPECTED_RESULTS.V1_1_0.polygon);
+ });
+ it('MultiPolygon to GML 1.1.0', () => {
+ const gmlMultiPolygon = processOGCGeometry(V1_1_0, multiPolygon);
+ expect(gmlMultiPolygon).toBe(EXPECTED_RESULTS.V1_1_0.multiPolygon);
+ });
+});
diff --git a/web/client/utils/ogc/__tests__/WFS-T-test.js b/web/client/utils/ogc/__tests__/WFS-T-test.js
new file mode 100644
index 0000000000..c8bb227883
--- /dev/null
+++ b/web/client/utils/ogc/__tests__/WFS-T-test.js
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017, GeoSolutions Sas.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+const expect = require('expect');
+const {insert, transaction} = require('../WFST');
+const {featureTypeSchema} = require('../WFS/base');
+const describeStates = require('json-loader!../../../test-resources/wfs/describe-states.json');
+const describePois = require('json-loader!../../../test-resources/wfs/describe-pois.json');
+const wyoming = require('json-loader!../../../test-resources/wfs/Wyoming.json');
+const museam = require('json-loader!../../../test-resources/wfs/museam.json');
+const expectedInsertWyoming = require('raw-loader!../../../test-resources/wfst/insert/Wyoming_1_1_0.xml');
+const expectedInsertmuseam = require('raw-loader!../../../test-resources/wfst/insert/museam_1_1_0.xml');
+
+describe('Test WFS-T request bodies generation', () => {
+ it('WFS-T insert', () => {
+ const result = insert(wyoming, describeStates);
+ expect(result).toExist();
+ });
+ it('WFS-T transaction with insert polygon', () => {
+ const result = transaction([insert(wyoming, describeStates)], featureTypeSchema(describeStates));
+ expect(result).toExist();
+ expect(result + '\n').toEqual(expectedInsertWyoming);
+ });
+ it('WFS-T transaction with insert multypolygon', () => {
+ const result = transaction([insert(wyoming, describeStates)], featureTypeSchema(describeStates));
+ expect(result).toExist();
+ expect(result + '\n').toEqual(expectedInsertWyoming);
+ });
+ it('WFS-T transaction with insert point', () => {
+ const result = transaction([insert(museam, describePois)], featureTypeSchema(describePois));
+ expect(result).toExist();
+ expect(result + '\n').toEqual(expectedInsertmuseam);
+ });
+});