Minimal TinyOWS/OpenLayers WFS-T HowTo

0) Install PostGIS and TinyOWS

1) Create a spatial Database

createdb -U postgres tinyows
createlang -U postgres plpgsql tinyows
psql -U postgres -d tinyows < `pg_config --sharedir`/contrib/postgis-1.5/postgis.sql
psql -U postgres -d tinyows < `pg_config --sharedir`/contrib/postgis-1.5/spatial_ref_sys.sql

2) Import Frida data (we will use the parks layer)

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 -s 31467 -W LATIN1 -I gruenflaechen.shp frida | psql -U postgres -d tinyows

3) Configure TinyOWS (/usr/local/tinyows/config.xml)

<tinyows online_resource="http://127.0.0.1/cgi-bin/tinyows"
         schema_dir="/usr/local/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="http://www.tinyows.org/"
           email="tinyows-users@lists.maptools.org" />

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

</tinyows>

4) Check PostGIS ans Tinyows config:

./YOUR_CGI-BIN_PATH/tinyows --check
Config File:	 OK
PG Connection:	 OK
Available layers:
 - public.frida  -> 31467 RW

5) Install OpenLayers

wget http://openlayers.org/download/OpenLayers-2.9.tar.gz
tar xvzf OpenLayers-2.9.tar.gz
mv OpenLayers-2.9  /YOUR/SERVER/HTDOCS/

6) Deploy OpenLayers proxy

cp OpenLayers-2.9/examples/proxy.cgi  /YOUR/SERVER/CGI-BIN/

7) Add localhost (or your server IP) to proxy allowedHosts (/YOUR/SERVER/CGI-BIN/proxy.cgi)

allowedHosts = ['127.0.0.1', 'www.openlayers.org', 'openlayers.org', ... ]

8) Create a new file named OpenLayers-2.9/examples/tinyows_wfs-t.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_wfs-t.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_wfs-t.js" target="_blank">

                wfs-protocol-transactions.js source</a> to see how this is done.
            </p>        
        </div>
    </body>
</html>

9) Create a new file named OpenLayers-2.9/examples/tinyows_wfs-t.js:

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 occured 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 :  "http://www.tinyows.org/",
            featureType: "frida",
            geometryName: "the_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();
}

10) Just test and play with !

You should obtain something like: http://tinyows.org/tracdocs/demo/OpenLayers-2.9/examples/tinyows_wfs-t.html

11) Think about Optimization