Skip to content
Fetching latest commit…
Cannot retrieve the latest commit at this time.
..
Failed to load latest commit information.
README.html
README.t2t
generate_docs.sh
qgis-footer.png
qgis-footer.xcf
qgisdoc.css
sample_wms_requests.txt

README.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<META NAME="generator" CONTENT="http://txt2tags.sf.net">
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>Quantum GIS (QGIS) Web Client</TITLE>

<!-- CSS include failed for style.css -->

</HEAD>
<BODY>

<DIV CLASS="header" ID="header">
<H1>Quantum GIS (QGIS) Web Client</H1>
<H2>Installation Guide</H2>
<H3>Tuesday April 24, 2012</H3>
</DIV>

<DIV CLASS="body" ID="body">
<P>
Last Updated: Tuesday April 24, 2012
Last Change : Tuesday April 24, 2012
</P>
<DIV CLASS="toc" ID="toc">
  <OL>
  <LI><A HREF="#toc1">For the terminally lazy</A>
  <LI><A HREF="#toc2">Purpose</A>
  <LI><A HREF="#toc3">Installation</A>
  <LI><A HREF="#toc4">Configuration of Client</A>
    <UL>
    <LI><A HREF="#toc5">4.1. Per Project Startup Options</A>
    <LI><A HREF="#toc6">4.2. Configuration of search panels</A>
    </UL>
  <LI><A HREF="#toc7">URL Rewriting</A>
  <LI><A HREF="#toc8">Configuration of search python script</A>
    <UL>
    <LI><A HREF="#toc9">6.1. Configuration of mod_wsgi</A>
    <LI><A HREF="#toc10">6.2. Adaption of the wsgi scripts to your settings and needs</A>
    </UL>
  <LI><A HREF="#toc11">PostgreSQL table setup for searching</A>
  <LI><A HREF="#toc12">License</A>
  <LI><A HREF="#toc13">Acknowledgements</A>
  </OL>

</DIV>
<P></P>
<A NAME="toc1"></A>
<H1>1. For the terminally lazy</H1>
<div class="code"><PRE>
sudo apt-get install apache2 libapache2-mod-fcgid
cp apache-conf/qgis-web-client.conf.tmpl apache-conf/qgis-web-client.conf
</PRE></div>
<P></P>
<P>
Update the paths in the copied file then:
</P>
<div class="code"><PRE>
cd /etc/apache2/sites-available/
ln -s &lt;path to apache-conf/qgis-web-client.conf&gt; .
sudo a2enmode rewrite
sudo a2ensite qgis-web-client.conf
sudo /etc/init.d/apache2 reload
</PRE></div>
<P></P>
<OL>
<LI>Check the symlink in cgi-bin is correct.
<LI>Check the QGIS libs are in your /etc/ld.so.conf path
<LI>Copy site/index.xml and check paths match your system OR
  Modify index.html and point your browser to that
</OL>

<P></P>
<A NAME="toc2"></A>
<H1>2. Purpose</H1>
<P>
A WMS based webgis client that makes use of QGIS specific WMS extensions (e.g.
highlighting, printing, metadata, etc.). QGIS webclient reads the configuration
from the WMS GetCapabilities command and builds the layer tree accordingly.
Supports legend graphic, feature info requests and printing.
</P>
<P>
The client builds on existing Web-GIS libraries OpenLayers and GeoExt, as well
as ExtJS 3 for the GUI widgets.
</P>
<P>
All major browsers should be supported.
</P>
<A NAME="toc3"></A>
<H1>3. Installation</H1>
<P>
Requirements (Server):
</P>
<UL>
<LI>Apache2 - Webserver (Ubuntu: apache2)
<LI>mod-fcgid (Ubuntu: libapache2-mod-fcgid)
<LI>QGIS and QGIS Server (best installed from source)
</UL>

<P></P>
<P>
On ubuntu you can meet these requirements by simply doing:
</P>
<div class="code"><PRE>
sudo apt-get install libapache2-mod-fcgid
</PRE></div>
<P></P>
<P>
The QGIS server compilation and installation will be covered in the QGIS manual.
</P>
<P>
For searching:
</P>
<UL>
<LI>python-wsgi for searching (Ubuntu: libapache2-mod-wsgi)
<LI>psycopg2 PostgreSQL db driver (Ubuntu: python-psycopg2)
<LI>webob - Python module providing WSGI request and response objects (Ubuntu:
  python-webob)
</UL>

<P></P>
<P>
The client part needs to be git cloned with the following command:
git clone <A HREF="https://github.com/qgis/qgis-web-client.git">https://github.com/qgis/qgis-web-client.git</A>
</P>
<A NAME="toc4"></A>
<H1>4. Configuration of Client</H1>
<P>
Global Settings for all projects (make a copy from one of the templates 
provided):
</P>
<div class="code"><PRE>
site/js/GlobalOptions.js
</PRE></div>
<P></P>
<P>
Translations (additional languages):
</P>
<div class="code"><PRE>
site/js/Translations.js
</PRE></div>
<P></P>
<P>
Project settings and index:
</P>
<div class="code"><PRE>
site/index.xml or site/index.html
</PRE></div>
<P></P>
<P>
Stylesheet of project index:
</P>
<div class="code"><PRE>
site/gis-project_listing.xsl
</PRE></div>
<P>
Thumbnails for individual projects (if you take the index.xml route):
</P>
<div class="code"><PRE>
thumbnails/projectname.png
</PRE></div>
<A NAME="toc5"></A>
<H2>4.1. Per Project Startup Options</H2>
<P>
These options are usually defined in the file site/index.xml. The stylesheet then generates the startup URL parameter options. On the client, the file <CODE>site/js/GetUrlParams.js</CODE> is responsible for the proper handling of the startup options. The following options are available:
</P>
<div class="code"><PRE>
lang
</PRE></div>
<P>
An optional language parameter. Normally, this parameter is defined in the file <CODE>site/js/GlobalOptions.js</CODE>. Optionally this can be overwritten on a per project base. Allowed values are two-character language codes that must be present in the file <CODE>site/js/Translations.js</CODE>.
</P>
<div class="code"><PRE>
visibleLayers
</PRE></div>
<P>
A comma-separated list of layers that should be set visible on the project start.
</P>
<div class="code"><PRE>
format
</PRE></div>
<P>
This optional parameter allows a per project definition of the file format. Valid values are <CODE>image/png</CODE>, <CODE>image/jpeg</CODE> and <CODE>image/png;mode=8bit</CODE>. Defaults to <CODE>image/png</CODE> if no format is given per project.
</P>
<div class="code"><PRE>
fullColorLayers
</PRE></div>
<P>
An optional comma-separated list of layers that need to be in full color (24bit). This parameter is only relevant if the project default image format is set to <CODE>image/png;mode=8bit</CODE>. If any of the layers in the fullColorLayers parameter list is set visible, the format changes to <CODE>image/jpeg</CODE>.
</P>
<div class="code"><PRE>
maxExtent
</PRE></div>
<P>
The maximum extent of the project. This parameter is used if the <CODE>Full View</CODE> navigation button is clicked. If the <CODE>startExtent</CODE> parameter is not specified, <CODE>maxExtent</CODE> will also be used as the <CODE>startExtent</CODE>. The format is: left,bottom,right,top in map units.
</P>
<div class="code"><PRE>
startExtent
</PRE></div>
<P>
The initial extent on project load if the project should start with a given, but not the maximum extent (e.g. for zooming to a specific project area). Not to be confused with the <CODE>maxExtent</CODE> parameter. The format is: left,bottom,right,top in map units.
</P>
<div class="code"><PRE>
searchtables
</PRE></div>
<P>
An optional list of additional search tables specific to the project. The format is <CODE>schemaname.tablename</CODE>. These additional search tables will be used for the search field at the top-right corner of the Webclient-GUI. The default search tables are hard-coded in the file <CODE>wsgi/search.wsgi</CODE>, in the <CODE>searchtables</CODE> array.
</P>
<A NAME="toc6"></A>
<H2>4.2. Configuration of search panels</H2>
<P>
There are two types of search panels supported, using a direct WMS
GetFeatureInfo request or using URL rewriting with a much shorter search URL.
</P>
<P>
The search panels are configured in <CODE>site/js/GlobalOptions.js</CODE>. 
</P>
<H3>4.2.1. Using WMS GetFeatureInfo</H3>
<div class="code"><PRE>
var simpleWmsSearch = {
  title: "Search continent",
  query: 'simpleWmsSearch',
  useWmsRequest: true,
  queryLayer: "Country",
  formItems: [
    {
      xtype: 'textfield',
      name: 'name',
      fieldLabel: "Name",
      allowBlank: false,
      blankText: "Please enter a name (e.g. 'africa')"
    }
  ],
  gridColumns: [
    {header: 'Name', dataIndex: 'name', menuDisabled: 'true'}
  ],
  selectionLayer: 'Country',
  selectionZoom: 0
};
</PRE></div>
<P></P>
<UL>
<LI><CODE>title</CODE>: title of the search tab
<LI><CODE>query</CODE>: identifier for this search
<LI><CODE>useWmsRequest</CODE>: enabled for WMS GetFeatureInfo request
<LI><CODE>queryLayer</CODE>: name of query layer
<LI><CODE>formItems</CODE>: list of Ext.form.FormPanel item configs
 <UL>
 <LI><CODE>xtype</CODE>: form field type
 <LI><CODE>name</CODE>: name of query layer attribute
 <LI><CODE>fieldLabel</CODE>: visible text for this field
 <LI><CODE>blankText</CODE>: popup text for blank fields
 </UL>
<LI><CODE>gridColumns</CODE>: list of Ext.grid.GridPanel column configs to show search
  results
<LI><CODE>selectionLayer</CODE>: name of layer for marking selected results (the same as
  <CODE>queryLayer</CODE>)
<LI><CODE>selectionZoom</CODE>: zoom level for jump-to when selecting results
</UL>

<P></P>
<P>
Request URL:
</P>
<P>
When performing a search query using the above configuration,
the following get request will be made.
</P>
<P>
``<A HREF="http://localhost/wms/helloworld?SERVICE=WMS&amp;VERSION=1.1.1&amp;">http://localhost/wms/helloworld?SERVICE=WMS&amp;VERSION=1.1.1&amp;</A>
REQUEST=GetFeatureInfo&amp;LAYERS=Country&amp;QUERY_LAYERS=Country&amp;
FEATURE_COUNT=10&amp;INFO_FORMAT=text/xml&amp;SRS=EPSG:4326&amp;
FILTER=Country:"name"+=+'africa'``
</P>
<H3>4.2.2. Using URL Rewriting</H3>
<P>
For security and neatness, you may prefer to use rewritten URLs (so that your
internal server file paths are not revealed. In that case your options file
would contain something like this:
</P>
<div class="code"><PRE>
var urlRewriteSearch = {
  title: "Search letter",
  query: 'samplesearch',
  formItems: [
    {
      xtype: 'hidden',
      name: 'query',
      value: 'samplesearch'
    },
    {
      xtype: 'textfield',
      name: 'colour',
      fieldLabel: "Colour",
      allowBlank: false,
      blankText: "Please enter a colour (e.g. 'orange')"
    }
  ],
  gridColumns: [
    {header: 'PKUID', dataIndex: 'pkuid', menuDisabled: 'true'},
    {header: 'Colour', dataIndex: 'colour', menuDisabled: 'true'}
  ],
  selectionLayer: 'Hello',
  selectionZoom: 1
};
</PRE></div>
<P></P>
<UL>
<LI><CODE>title</CODE>: title of the search tab
<LI><CODE>query</CODE>: identifier for this search
<LI><CODE>formItems</CODE>: list of Ext.form.FormPanel item configs, the <CODE>query</CODE> form
  field is required to match the rewrite rule (value is the same as <CODE>query</CODE>)
 <UL>
 <LI><CODE>xtype</CODE>: form field type
 <LI><CODE>name</CODE>: name of query layer attribute
 <LI><CODE>fieldLabel</CODE>: visible text for this field
 <LI><CODE>blankText</CODE>: popup text for blank fields
 </UL>
<LI><CODE>gridColumns</CODE>: list of Ext.grid.GridPanel column configs to show search
  results
<LI><CODE>selectionLayer</CODE>: name of layer for marking selected results
<LI><CODE>selectionZoom</CODE>: zoom level for jump-to when selecting results
</UL>

<P></P>
<P>
For every search of this type you have to add a URL rewrite rule in the Apache
config. *Note:* Linebreaks added for formatting - they should be removed in your
config file.
</P>
<div class="code"><PRE>
RewriteCond %{QUERY_STRING} ^(?:.*)query=samplesearch&amp;*(?:.*)$
RewriteCond %{QUERY_STRING} ^(?:(?:.*)&amp;)?colour=([^&amp;]*)(?:.*)$
RewriteRule ^/wms/(.+)$ /cgi-bin/qgis_mapserv.fcgi?map=/
&lt;path-to-qgis-server-projects&gt;/$1.qgs&amp;SERVICE=WMS&amp;VERSION=1.1.1&amp;
REQUEST=GetFeatureInfo&amp;LAYERS=Hello&amp;QUERY_LAYERS=Hello&amp;FEATURE_COUNT=20&amp;
INFO_FORMAT=text/xml&amp;SRS=EPSG:4326&amp;FILTER=Hello:"colour"\ =\ '%1' [PT]
</PRE></div>
<P></P>
<P>
The first RewriteCond matches the <CODE>query</CODE> id of the search panel config. The
second RewriteCond extracts the values of the search request parameters.
</P>
<P>
The RewriteRule composes the actual WMS GetFeatureInfo request to QGIS mapserver.
</P>
<P>
Request URL:
</P>
<P>
<CODE>http://localhost/wms/helloworld?query=samplesearch&amp;colour=orange</CODE>
</P>
<H3>4.2.3. Add search panels to projects</H3>
<P>
In order for your search panel to appear in the web UI, you must
enumerate them in your GlobalOptions.js for example (with url rewriting):
</P>
<div class="code"><PRE>
var mapSearchPanelConfigs = {
  "helloworld": [simpleWmsSearch, urlRewriteSearch]
};
</PRE></div>
<P></P>
<P>
Example (no rewriting):
</P>
<div class="code"><PRE>
var mapSearchPanelConfigs = {
  "../projects/helloworld.qgs": [simpleWmsSearch, urlRewriteSearch]
};
</PRE></div>
<P></P>
<P>
Search panels are added to a project by adding a new key for the map name
with a list of search panel configs to <CODE>mapSearchPanelConfigs</CODE>.  If there is
no search panel configuration for a project, the search will be hidden in the
GUI.
</P>
<P>
The map name is whatever is passed in the get request for your .qgs file. For
example if your url includes this:
</P>
<div class="code"><PRE>
http://localhost/cgi-bin/qgis_mapserv.fcgi?map=../projects/helloworld.qgs
</PRE></div>
<P></P>
<P>
then your mapSearchPanelConfigs should reflect ../projects/helloworld.qgs as
the key for the search list.
</P>
<A NAME="toc7"></A>
<H1>5. URL Rewriting</H1>
<P>
Using a standard installation of QGIS server, GlobalOptions.js will have a WMS
server configuration like
</P>
<div class="code"><PRE>
var serverAndCGI = "/cgi-bin/qgis_mapserv.fcgi";
</PRE></div>
<P></P>
<P>
A sample URL for QGIS Web Client installed in /var/www/qgis-web-client:
</P>
<div class="code"><PRE>
http://localhost/qgis-web-client/qgiswebclient.html?map=/opt/geodata/maps/NaturalEarth.qgs&amp;visibleLayers=HYP_50M_SR_W
</PRE></div>
<P></P>
<P>
With the following rules for Apache mod_rewrite you can shorten the URLs to
</P>
<div class="code"><PRE>
var serverAndCGI = "/wms";
</PRE></div>
<P>
and
</P>
<div class="code"><PRE>
http://localhost/maps/NaturalEarth?visibleLayers=HYP_50M_SR_W
</PRE></div>
<P></P>
<P>
Rules in VirtualHost configuration:
</P>
<div class="code"><PRE>
# Forbid direct access
RewriteRule ^/cgi-bin/.*$ - [F]

# Search with SearchPanel (e.g. Address)
RewriteCond %{QUERY_STRING} ^(?:.*)query=address&amp;*(?:.*)$
RewriteCond %{QUERY_STRING} ^(?:(?:.*)&amp;)?street=([^&amp;]*)(?:(?:.*)&amp;)+number=([^&amp;]*)(?:.*)$
RewriteRule ^/wms/(.+)$ /cgi-bin/qgis_mapserv.fcgi?map=/opt/geodata/maps/$1.qgs&amp;SERVICE=WMS&amp;VERSION=1.1.1&amp;REQUEST=GetFeatureInfo&amp;LAYERS=addresses&amp;QUERY_LAYERS=addresses&amp;FEATURE_COUNT=10&amp;INFO_FORMAT=text/xml&amp;SRS=EPSG:21781&amp;FILTER=addresses:"street"\ =\ '%1' AND "number"\ =\ %2 [PT]

# Rewrite /wms/mapname to qgis_mapserv.fcgi?map=mappath/mapname.qgs
RewriteRule ^/wms/(.+)$ /cgi-bin/qgis_mapserv.fcgi?map=/opt/geodata/maps/$1.qgs [QSA,PT]
# Rewrite /maps/mapname to qgis-web-client main page. mapname will be extracted for wms calls in Javascript code.
RewriteRule ^/maps/([^\.]+)$ /qgis-web-client/site/qgiswebclient.html [PT]
# Rewrite /maps/* to qgis-web-client/site (e.g. /maps/gis_icons/mActionZoomNext.png -&gt; /qgis-web-client/site/gis_icons/mActionZoomNext.png)
RewriteRule ^/maps/(.*) /qgis-web-client/site/$1 [PT]
</PRE></div>
<P></P>
<P>
For supporting qgs files in subdirectories (e.g. /maps/subdir/mapnampe) replace last rule with:
</P>
<div class="code"><PRE>
RewriteRule ^/maps/[^/]+/(.*) /qgis-web-client/site/$1 [PT]
</PRE></div>
<P></P>
<P>
For adding zones in different subdirecories (e.g. maps and maps-protected) add the following rules:
</P>
<div class="code"><PRE>
RewriteRule ^/wms-protected/(.+)$ /cgi-bin/qgis_mapserv.fcgi?map=/opt/geodata/maps-protected/$1.qgs [QSA,PT]
RewriteRule ^/maps-protected/([^\.]+)$ /qgis-web-client/site/qgiswebclient.html [PT]
RewriteRule ^/maps-protected/(.*) /qgis-web-client/site/$1 [PT]
</PRE></div>
<P></P>
<A NAME="toc8"></A>
<H1>6. Configuration of search python script</H1>
<P>
Searching is handled by two separate python-wsgi scripts: "search.wsgi" lists
back a hit list while the user is typing in the searchbox. It groups the
results and returns a bounding box of the result. "getSearchGeom.wsgi" returns
the actual wkt geometry for a selected search result. It is recommended to
install the wsgi scripts in a separate directory, e.g. /home/www/wsgi, a place
that is not reachable by regular web traffic.
</P>
<A NAME="toc9"></A>
<H2>6.1. Configuration of mod_wsgi</H2>
<P>
You need to enable mod_wsgi as root. (Ubuntu: a2enmod mod_wsgi).
</P>
<P>
You need to configure apache with the following lines (e.g. in file
/etc/apache2/sites-available/default):
</P>
<div class="code"><PRE>
#mod_wsgi
WSGIDaemonProcess gis processes=5 threads=15 display-name=%{GROUP}
WSGIScriptAlias /wsgi/ /home/www/wsgi/
WSGIScriptAliasMatch ^/wsgi/([^/]+) /home/www/wsgi/$1.wsgi
</PRE></div>
<P></P>
<A NAME="toc10"></A>
<H2>6.2. Adaption of the wsgi scripts to your settings and needs</H2>
<H3>6.2.1. DB connection</H3>
<P>
In the files "search.wsgi" and "getSearchGeom.wsgi" please edit the line
containing the db connection strings. Search for the line 
</P>
<div class="code"><PRE>
conn = psycopg2.connect("host='yourhost' dbname='yourdb' port='5432' user='yourusername' password='yourpassword'")
</PRE></div>
<P></P>
<P>
and adapt the parameters according to your server/db.
</P>
<H3>6.2.2. Search type to be used</H3>
<P>
The search can use PostgreSQL's tsvector data type. 
"A tsvector value is a sorted list of distinct lexemes, which are words that have been normalized to merge different variants of the same word."
(from the doc at <A HREF="http://www.postgresql.org/docs/9.0/interactive/datatype-textsearch.html#DATATYPE-TSVECTOR">http://www.postgresql.org/docs/9.0/interactive/datatype-textsearch.html#DATATYPE-TSVECTOR</A>)
Thus tsvector skips all the fill words and reduces nouns to their single form, a behaviour useful for searching texts.
However as we are normally dealing with <B>place names</B> here we want them to stay as they are. If you use a language where the single form is a lot different
from the plural form but your name contains a plural you will not get a suitable result.
If you want to use the tsvector search option you should activate the line
</P>
<div class="code"><PRE>
sql += "searchstring_tsvector @@ to_tsquery(\'not_your_language\', '"+querystrings[j]+":*')"
</PRE></div>
<P></P>
<P>
<I>not_your_language</I> is to be replaced with an entry e.g. <I>finnish</I> if you have German place names.
Thus plural forms and fillwords are kept as they are. Be aware of side effects!
Be sure to fill the field <I>searchstring_tsvector</I> with <CODE>`to_tsvector('not_your_language', 'yourstring')`</CODE>.
</P>
<P>
The use of
</P>
<div class="code"><PRE>
sql += "searchstring::tsvector @@ lower('"+querystrings[j]+":*')::tsquery"
</PRE></div>
<P></P>
<P>
is <B>discouraged</B> as it does not find a place name like <I>Stoke-sub-Hamden</I> when you enter <I>Stoke</I>.
</P>
<P>
If you do not want to use tsvector at all you can enable the full string comparison on the field <I>searchstring</I> (activated by default).
</P>
<div class="code"><PRE>
sql += "searchstring ILIKE \'%"+querystrings[j]+"%\'"
</PRE></div>
<P></P>
<P>
This method however is slower than tsvector but not relevantly at least if you only have a couple 1000 datasets. 
</P>
<A NAME="toc11"></A>
<H1>7. PostgreSQL table setup for searching</H1>
<div class="code"><PRE>
CREATE TABLE cadastre.searchtable
(
  searchstring text, --the search string (all lower case), e.g. "zürichstrasse 46, 8610 uster"
  displaytext text NOT NULL, --the display text for the search combobox, e.g. "Zürichstrasse 46, 8610 Uster (address)"
  search_category text, --should have a leading two digit number:, e.g.
                        --"03_parcels", where 03 is the order of the search categories, the number
                        --should be unique across all search tables
  the_geom geometry,    --the actual geometry
  geometry_type text,   --the geometry type as returned by ST_GeometryType(the_geom)
  searchstring_tsvector tsvector, -- be sure to fill this with to_tsvector()
  CONSTRAINT searchtable_pkey PRIMARY KEY (displaytext)
)
WITH (
  OIDS=FALSE
);
GRANT SELECT ON TABLE cadastre.searchtable TO alle;

-- Index: cadastre.in_cadastre_searchstring_tsvector_gin

CREATE INDEX in_cadastre_searchstring_tsvector_gin
  ON cadastre.searchtable
  USING gin
  (searchstring_tsvector);
</PRE></div>
<P></P>
<P>
The above search table can also be a view or materialized view. One can combine
several search tables by specifying the <CODE>`searchtables=searchtable1,searchtable2,searchtable3`</CODE> 
parameter when requesting the <I>search.wsgi</I> script.
</P>
<P>
Using views is generally slower than properly indexed tables, check for yourself what works best.
</P>
<A NAME="toc12"></A>
<H1>8. License</H1>
<P>
BSD
</P>
<A NAME="toc13"></A>
<H1>9. Acknowledgements</H1>
<P>
We'd like to thank the OpenLayers and GeoExt team for providing their base libraries.
</P>
</DIV>

<!-- html code generated by txt2tags 2.5 (http://txt2tags.sf.net) -->
<!-- cmdline: txt2tags -o README.html -t html README.t2t -->
</BODY></HTML>
Something went wrong with that request. Please try again.