Sample: WFS-T with TinyOWS and OpenLayers

  1. Install PostGIS and TinyOWS (TinyOWS Installation)

  2. Within PostGIS, create a spatial database called «tinyows»

createdb -U postgres tinyows
psql -U postgres -d tinyows < `pg_config --sharedir`/contrib/postgis-2.0/postgis.sql
psql -U postgres -d tinyows < `pg_config --sharedir`/contrib/postgis-2.0/spatial_ref_sys.sql
  1. Import Frida data (we will use the parks layer) into your PostGIS database

wget ftp://ftp.intevation.de/freegis/frida/frida-1.0.1-shp.tar.gz
tar xvzf frida-1.0.1-shp.tar.gz
cd frida-1.0.1-shp
shp2pgsql -g geom -s 31467 -W LATIN1 -I gruenflaechen.shp frida | psql -U postgres -d tinyows
  1. Configure TinyOWS by editing /usr/local/tinyows/config.xml

<tinyows online_resource="http://127.0.0.1/cgi-bin/tinyows"
         schema_dir="/usr/local/share/tinyows/schema/">

<pg host="127.0.0.1" user="postgres" password="postgres" dbname="tinyows" port="5432"/>

<metadata name="TinyOWS Server"
          title="TinyOWS Server - WFS-T Frida Service" />

<contact name="TinyOWS Server"
         site="https://mapserver.org/tinyows/"
         email="tinyows-users@lists.maptools.org" />

<layer retrievable="1"
       writable="1"
       ns_prefix="tows"
       ns_uri="https://mapserver.org/tinyows/"
       name="frida"
       title="Frida Parks" />

</tinyows>
  1. Test your installations of TinyOWS and PostGIS

./YOUR_CGI-BIN_PATH/tinyows --check
TinyOWS version:   1.2.0
FCGI support:      Yes
Config File Path:  /etc/tinyows.xml (TinyOWS XML)
PostGIS Version:   3.1.1
PostGIS dsn:       host=127.0.0.1 user=postgres password=postgres dbname=tinyows port=5432
Output Encoding:   UTF-8
Database Encoding: UTF8
Schema dir:        /usr/local/share/tinyows/schema/
Display bbox:      Yes
Estimated extent:  No
Check schema:      Yes
Check valid geoms: Yes
Available layers:
 - public.frida (31467) -> tows:frida [RW]
  1. Install OpenLayers

wget http://openlayers.org/download/OpenLayers-2.12.tar.gz
tar xvzf OpenLayers-2.12.tar.gz
sudo mv OpenLayers-2.12  /YOUR/SERVER/HTDOCS/
  1. Install the OpenLayers proxy (you need the Python interpreter to make it work)

sudo cp OpenLayers-2.12/examples/proxy.cgi  /YOUR/SERVER/CGI-BIN/
  1. Add your server IP to proxy allowedHosts in (/YOUR/SERVER/CGI-BIN/proxy.cgi)

allowedHosts = ['127.0.0.1', 'www.openlayers.org', 'openlayers.org', ... ]
  1. Create a new file at OpenLayers-2.12/examples/tinyows.html

<html>
<head>
  <link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
  <link rel="stylesheet" href="style.css" type="text/css" />
  <script src="../lib/OpenLayers.js"></script>
  <style>
      #map {
          width: 800px;
          height: 500px;
          float: left;
          border: 1px solid #ccc;
      }
      #message {
          position: relative;
          left: 5px;
      }
      #docs {
          float: left;
      }
      .customEditingToolbar {
          float: right;
          right: 0px;
          height: 30px;
          width: 200px;
      }
      .customEditingToolbar div {
          float: right;
          margin: 5px;
          width: 24px;
          height: 24px;
      }
      .olControlNavigationItemActive {
          background-image: url("../theme/default/img/editing_tool_bar.png");
          background-repeat: no-repeat;
          background-position: -103px -23px;
      }
      .olControlNavigationItemInactive {
          background-image: url("../theme/default/img/editing_tool_bar.png");
          background-repeat: no-repeat;
          background-position: -103px -0px;
      }
      .olControlDrawFeaturePolygonItemInactive {
          background-image: url("../theme/default/img/editing_tool_bar.png");
          background-repeat: no-repeat;
          background-position: -26px 0px;
      }
      .olControlDrawFeaturePolygonItemActive {
          background-image: url("../theme/default/img/editing_tool_bar.png");
          background-repeat: no-repeat;
          background-position: -26px -23px ;
      }
      .olControlModifyFeatureItemActive {
          background-image: url(../theme/default/img/move_feature_on.png);
          background-repeat: no-repeat;
          background-position: 0px 1px;
      }
      .olControlModifyFeatureItemInactive {
          background-image: url(../theme/default/img/move_feature_off.png);
          background-repeat: no-repeat;
          background-position: 0px 1px;
      }
      .olControlDeleteFeatureItemActive {
          background-image: url(../theme/default/img/remove_point_on.png);
          background-repeat: no-repeat;
          background-position: 0px 1px;
      }
      .olControlDeleteFeatureItemInactive {
          background-image: url(../theme/default/img/remove_point_off.png);
          background-repeat: no-repeat;
          background-position: 0px 1px;
      }
  </style>
  <script src="tinyows.js"></script>
  </head>
  <body onload="init()">
      <h1 id="title">WFS Transaction Example, (TinyOWS ans OpenLayers)</h1>
      <div id="tags"></div>
      <p id="shortdesc">
          Shows the use of the WFS Transactions (WFS-T).
          Parks of Osnabruck (Frida).
      <br />
          Base layers is OpenStreetMap from Omniscale WMS Server.
      </p>
      <div id="map"></div>

      <div id="message"></div>
      <div id="docs">
          <p>
              The WFS protocol allows for creation of new features and
              reading, updating, or deleting of existing features.
          </p>
          <p>
              Use the tools to create, modify, and delete (in order from left
              to right) features. Use the save tool (picture of a disk) to
              save your changes. Use the navigation tool (hand) to stop
              editing and use the mouse for map navigation.
          </p>
          <p>
              See the <a href="tinyows.js" target="_blank">
              wfs-protocol-transactions.js source</a> to see how this is done.
          </p>
      </div>
  </body>
</html>
  1. Create a new file at OpenLayers-2.12/examples/tinyows.js (and replace all 127.0.0.1 addresses by your IP server if necessary)

var map, wfs;
OpenLayers.ProxyHost = "/cgi-bin/proxy.cgi?url=";
var DeleteFeature = OpenLayers.Class(OpenLayers.Control, {
  initialize: function(layer, options) {
      OpenLayers.Control.prototype.initialize.apply(this, [options]);
      this.layer = layer;
      this.handler = new OpenLayers.Handler.Feature(
          this, layer, {click: this.clickFeature}
      );
  },
  clickFeature: function(feature) {
      // if feature doesn't have a fid, destroy it
      if(feature.fid == undefined) {
          this.layer.destroyFeatures([feature]);
      } else {
          feature.state = OpenLayers.State.DELETE;
          this.layer.events.triggerEvent("afterfeaturemodified",
                                         {feature: feature});
          feature.renderIntent = "select";
          this.layer.drawFeature(feature);
      }
  },
  setMap: function(map) {
      this.handler.setMap(map);
      OpenLayers.Control.prototype.setMap.apply(this, arguments);
  },
  CLASS_NAME: "OpenLayers.Control.DeleteFeature"
});
function showMsg(szMessage) {
  document.getElementById("message").innerHTML = szMessage;
  setTimeout(
      "document.getElementById('message').innerHTML = ''",2000);
}
function showSuccessMsg(){
  showMsg("Transaction successfully completed");
};
function showFailureMsg(){
  showMsg("An error occurred while operating the transaction");
};
function init() {
  map = new OpenLayers.Map('map', {
      projection: new OpenLayers.Projection("EPSG:31467"),
      units: "m",
      maxResolution: "auto",
      maxExtent: new OpenLayers.Bounds(3427000,5788000,3444000,5800000),
      controls: [
          new OpenLayers.Control.PanZoom()
      ]
  });
  var osm = new OpenLayers.Layer.WMS(
      "OSM by Omniscale WMS",
      "http://osm.omniscale.net/proxy/service",
      {layers: 'osm', format: 'image/jpeg'},
      {projection:"EPSG:31467",  units: "m", maxResolution: "auto", maxExtent: new OpenLayers.Bounds(3427000,5788000,3444000,5800000)}
  );
  var saveStrategy = new OpenLayers.Strategy.Save();
  saveStrategy.events.register("success", '', showSuccessMsg);
  saveStrategy.events.register("fail", '', showFailureMsg);
  wfs = new OpenLayers.Layer.Vector("Editable Features", {
      strategies: [new OpenLayers.Strategy.BBOX(), saveStrategy],
      projection: new OpenLayers.Projection("EPSG:31467"),
      protocol: new OpenLayers.Protocol.WFS({
          version: "1.1.0",
          srsName: "EPSG:31467",
          url: "http://127.0.0.1/cgi-bin/tinyows",
          featureNS :  "https://mapserver.org/tinyows/",
          featureType: "frida",
          geometryName: "geom",
          schema: "http://127.0.0.1/cgi-bin/tinyows?service=wfs&request=DescribeFeatureType&version=1.1.0&typename=tows:frida"
      })
  });
  map.addLayers([osm, wfs]);
  var panel = new OpenLayers.Control.Panel(
      {'displayClass': 'customEditingToolbar'}
  );
  var navigate = new OpenLayers.Control.Navigation({
      title: "Pan Map"
  });
  var draw = new OpenLayers.Control.DrawFeature(
      wfs, OpenLayers.Handler.Polygon,
      {
          title: "Draw Feature",
          displayClass: "olControlDrawFeaturePolygon",
          multi: true
      }
  );
  var edit = new OpenLayers.Control.ModifyFeature(wfs, {
      title: "Modify Feature",
      displayClass: "olControlModifyFeature"
  });
  var del = new DeleteFeature(wfs, {title: "Delete Feature"});
  var save = new OpenLayers.Control.Button({
      title: "Save Changes",
      trigger: function() {
          if(edit.feature) {
              edit.selectControl.unselectAll();
          }
          saveStrategy.save();
      },
      displayClass: "olControlSaveFeatures"
  });
  panel.addControls([navigate, save, del, edit, draw]);
  panel.defaultControl = navigate;
  map.addControl(panel);
  map.zoomToMaxExtent();
}