 <?xml-stylesheet type="text/css" href="https://www.esdm.co.uk/Data/style/rss1.css" ?> <?xml-stylesheet type="text/xsl" href="https://www.esdm.co.uk/Data/style/rss1.xsl" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
  <channel>
    <title>The knowledge base blog</title>
    <link>https://www.esdm.co.uk/knowledge</link>
    <description />
    <docs>http://www.rssboard.org/rss-specification</docs>
    <generator>mojoPortal Blog Module</generator>
    <language>en-GB</language>
    <ttl>120</ttl>
    <atom:link href="https://www.esdm.co.uk/Blog/RSS.aspx?p=138~108~7" rel="self" type="application/rss+xml" />
    <itunes:owner />
    <itunes:explicit>no</itunes:explicit>
    <item>
      <title>Opening ESRI Personal Geodatabases in QGIS</title>
      <description><![CDATA[<p>In the more recent 64-bit versions of QGIS it is no&nbsp;longer possible&nbsp;to open ESRI personal geodatabases using the 'Add vector layer' functionality. However, it is possible to enable access to personal geodatabases by doing the following:</p>

<ol>
	<li>Download the 64-bit version on the MS Access ODBC driver (AccessDatabaseEngine_x64.exe): <a href="http://www.microsoft.com/en-gb/download/details.aspx?id=13255">http://www.microsoft.com/en-gb/download/details.aspx?id=13255</a></li>
	<li>If you <strong>don’t</strong> have 32-bit MS Office installed you can just run the exe, however if you <strong>do</strong> have 32-bit office installed then you need to start an admin command prompt and run the installer in passive mode as follows:</li>
</ol>

<pre class="Indent1">
AccessDatabaseEngine_x64.exe /passive</pre>

<ol start="3">
	<li>For QGIS 2: Then you need to edit the qgis-ltr.bat (normally in C:\Program Files\QGIS x.xx\bin, in previous versions used to be qgis.bat) and add the following lines (before the ‘start’ command)</li>
</ol>

<pre class="Indent1">
set OGR_SKIP=ODBC
set PGEO_DRIVER_TEMPLATE=DRIVER=Microsoft Access Driver (*.mdb, *.accdb);DBQ=%%s</pre>

<ol start="4">
	<li>For QGIS 3: In QGIS Settings panel -&gt; Options | System | Environment add the following two variables:
	<p>variable name: PGEO_DRIVER_TEMPLATE&nbsp;<br />
	value: DRIVER=Microsoft Access Driver (*.mdb, *.accdb);DBQ=%s<br />
	Or if being used on a shared server add a system environment variable&nbsp;<br />
	PGEO_DRIVER_TEMPLATE=DRIVER=Microsoft Access Driver (*.mdb, *.accdb);DBQ=%s</p>
	</li>
	<li>
	<div>Now you should be able to start QGIS and simply drag the geodatabase mdb file onto the QGIS workspace and it will open.</div>
	</li>
</ol>

<div>Note: This solution has been tested using QGIS v2.14.11 (Essen)&nbsp; &amp; QGIS v3.4.13 (Madeira)</div>

<div>Note: This post is based on information taken from the web page listed below, however that page&nbsp;was based on an older version of QGIS (2.8.1) which used the qgis.bat file rather than qgis-ltr.bat.</div>

<div><a href="http://gis.stackexchange.com/questions/129514/opening-esri-personal-geodatabase-mdb-using-qgis">http://gis.stackexchange.com/questions/129514/opening-esri-personal-geodatabase-mdb-using-qgis</a></div>
<br /><a href='https://www.esdm.co.uk/opening-esri-personal-geodatabases-in-qgis'>Steve Ellwood</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/opening-esri-personal-geodatabases-in-qgis'>...</a>]]></description>
      <link>https://www.esdm.co.uk/opening-esri-personal-geodatabases-in-qgis</link>
      <author>stevee@esdm.co.uk (Steve Ellwood)</author>
      <comments>https://www.esdm.co.uk/opening-esri-personal-geodatabases-in-qgis</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/opening-esri-personal-geodatabases-in-qgis</guid>
      <pubDate>Wed, 01 Mar 2017 10:13:00 GMT</pubDate>
    </item>
    <item>
      <title>How to hide the MapServer map file location on IIS (without writing any code)</title>
      <description><![CDATA[<p>We posted some notes a few days ago on <a href="https://www.esdm.co.uk/how-to-install-the-latest-version-of-mapserver-on-windows" target="_blank">how to set up MapServer WMS/WFS server on IIS</a>. Someone called Dennis has commented appreciatively, but asked how to hide the location of the .map file and <a href="http://mapserver.org/ogc/wms_server.html#changing-the-online-resource-url" target="_blank">create a cleaner URL with a cgi-wrapper, as described in the MapServer documentation</a>. Well that documentation page does tell you how to do it on IIS, with ASP script, but here’s a much simpler way that requires no hacking.</p>

<p>Aside: I should say that what follows is not what we normally do in our real applications – we usually use ASP.Net .ashx handlers to relay requests through the root of our web sites, which also helps to avoid cross-domain problems with GetFeatureInfo responses and WFS, but that’s another story! This post is in reaction to Dennis’s request for help, and because I wanted to check this approach is viable.</p>

<p>I’ve posted before on <a href="https://www.esdm.co.uk/presenting-geoserver-on-port-80-on-a-windows-iis-server" target="_blank">how to use URL Rewrite to give GeoServer a friendly URL on IIS</a>, and we’re going to use the same technique here, so first ensure you have the necessaries installed.</p>

<p>You should also already have a working WMS/WFS – we created this one in the previous post:</p>

<p><a href="http://mapservertest.esdm.co.uk/scripts/mapserv.exe?map=D:\Websites\mapservertest\map\mapfiletest.map" title="http://mapservertest.esdm.co.uk/scripts/mapserv.exe?map=D:\Websites\mapservertest\map\mapfiletest.map">http://mapservertest.esdm.co.uk/scripts/mapserv.exe?map=D:\Websites\mapservertest\map\mapfiletest.map</a></p>

<p>which was fine, but a) the URL reveals some information about the structures on the web server, and b) the URL is a bit ugly.</p>

<p>So decide what address you want for your MapServer service (remembering this will be specific right down to the map file). I’m using “mapservertestwrapper.esdm.co.uk” which I have pointed at the web server in question.</p>

<p>Create a new web site in IIS with this URL as a host header (you’ll first need to create an empty “physical” folder for the site to point at – add a readme.config file explaining what it’s for).</p>

<p>Now open URL Rewrite and create a reverse proxy inbound rule, entering the real service address as the forwarding address:</p>

<p><img alt="Creating the reverse proxy inbound rule" border="0" height="480" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_97.png" style="border-width: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Creating the reverse proxy inbound rule" width="476" /></p>

<p>OK now we should be able to see our MapServer on the new URL. However, when we visit <a href="http://mapservertestwrapper.esdm.co.uk/" title="http://mapservertestwrapper.esdm.co.uk/">http://mapservertestwrapper.esdm.co.uk/</a> we get this feedback in the browser:</p>

<p>msLoadMap(): Regular expression error. MS_DEFAULT_MAPFILE_PATTERN validation failed. msEvalRegex(): Regular expression error. String failed expression test.</p>

<p>The fix is to re-edit the inbound rule, and remove the final “/” in the “rewrite URL”, going from:</p>

<p>http://mapservertest.esdm.co.uk/scripts/mapserv.exe?map=D:\Websites\mapservertest\map\mapfiletest.map/{R:1}</p>

<p>to:</p>

<p>http://mapservertest.esdm.co.uk/scripts/mapserv.exe?map=D:\Websites\mapservertest\map\mapfiletest.map{R:1}</p>

<p>Now we should get sensible MapServer feedback in the browser (conditioned by a template specified in the map file).</p>

<p>Now let’s see if we can do a GetCapabilities request… turns out our URL has to be this:</p>

<p><a href="http://mapservertestwrapper.esdm.co.uk/?service=wms&amp;version=1.3.0&amp;request=getcapabilities" title="http://mapservertestwrapper.esdm.co.uk/?service=wms&amp;version=1.3.0&amp;request=getcapabilities">http://mapservertestwrapper.esdm.co.uk/?service=wms&amp;version=1.3.0&amp;request=getcapabilities</a></p>

<p>Yes it works, but a) that’s a slightly odd URL with “/?”, and b) the response contains the original service addresses, again revealing information about our server.</p>

<p>To fix a), we go back and edit our inbound rule again, and change the “pattern” from “(.*)” to “map1(.*)” (without the quotes of course). Now our service URL becomes:</p>

<p><a href="http://mapservertestwrapper.esdm.co.uk/map1?service=wms&amp;version=1.3.0&amp;request=getcapabilities" title="http://mapservertestwrapper.esdm.co.uk/map1?service=wms&amp;version=1.3.0&amp;request=getcapabilities">http://mapservertestwrapper.esdm.co.uk/map1?service=wms&amp;version=1.3.0&amp;request=getcapabilities</a></p>

<p>Ah yes that’s much nicer, and now of course we see we can manage lots of .map files within one web site simply by setting up in inbound rule for each, with an appropriate pattern and target.</p>

<p>To fix b) we can edit the metadata in the .map file to set this:</p>

<p>"wms_onlineresource"&nbsp;&nbsp;&nbsp; "http://mapservertestwrapper.esdm.co.uk/map1"</p>

<p>This works fine, returning this within the GetCapabilities:</p>

<p><a>&lt;OnlineResource xlink:href="http://mapservertestwrapper.esdm.co.uk/map1?" xmlns:xlink="http://www.w3.org/1999/xlink"/&gt;</a></p>

<p>And I can confirm this creates a working map in QGIS, with map requests like this <a href="https://www.esdm.co.uk/inspecting-web-service-requests-from-quantum-gis-with-fiddler" target="_blank">visible in Fiddler</a>:</p>

<p><a href="http://mapservertestwrapper.esdm.co.uk/map1?SERVICE=WMS&amp;VERSION=1.3.0&amp;REQUEST=GetMap&amp;BBOX=52801.44371538766426966,3347.3560407145996578,657116.82161524775438011,428145.51874091033823788&amp;CRS=EPSG:27700&amp;WIDTH=1020&amp;HEIGHT=717&amp;LAYERS=grid&amp;STYLES=&amp;FORMAT=image/png&amp;DPI=96&amp;MAP_RESOLUTION=96&amp;FORMAT_OPTIONS=dpi:96&amp;TRANSPARENT=TRUE" title="http://mapservertestwrapper.esdm.co.uk/map1?SERVICE=WMS&amp;VERSION=1.3.0&amp;REQUEST=GetMap&amp;BBOX=52801.44371538766426966,3347.3560407145996578,657116.82161524775438011,428145.51874091033823788&amp;CRS=EPSG:27700&amp;WIDTH=1020&amp;HEIGHT=717&amp;LAYERS=grid&amp;STYLES=&amp;FORMAT=image/png&amp;DPI=96&amp;MAP_RESOLUTION=96&amp;FORMAT_OPTIONS=dpi:96&amp;TRANSPARENT=TRUE&#10;">http://mapservertestwrapper.esdm.co.uk/map1?SERVICE=WMS&amp;VERSION=1.3.0&amp;REQUEST=GetMap&amp;BBOX=52801.44371538766426966,3347.3560407145996578,657116.82161524775438011,428145.51874091033823788&amp;CRS=EPSG:27700&amp;WIDTH=1020&amp;HEIGHT=717&amp;LAYERS=grid&amp;STYLES=&amp;FORMAT=image/png&amp;DPI=96&amp;MAP_RESOLUTION=96&amp;FORMAT_OPTIONS=dpi:96&amp;TRANSPARENT=TRUE</a></p>

<p><img alt="image" border="0" height="426" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_98.png" style="border: 0px currentColor; border-image: none; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="image" width="640" /></p>

<p>And a quick test shows that info clicking (GetFeatureInfo) and legends work too:</p>

<p>e.g. <a href="http://mapservertestwrapper.esdm.co.uk/map1?version=1.3.0&amp;service=WMS&amp;request=GetLegendGraphic&amp;sld_version=1.1.0&amp;layer=grid&amp;format=image/png&amp;STYLE=default" title="http://mapservertestwrapper.esdm.co.uk/map1?version=1.3.0&amp;service=WMS&amp;request=GetLegendGraphic&amp;sld_version=1.1.0&amp;layer=grid&amp;format=image/png&amp;STYLE=default">http://mapservertestwrapper.esdm.co.uk/map1?version=1.3.0&amp;service=WMS&amp;request=GetLegendGraphic&amp;sld_version=1.1.0&amp;layer=grid&amp;format=image/png&amp;STYLE=default</a></p>

<p>gives this:</p>

<p><img alt="GetLegendGraphic image" src="http://mapservertestwrapper.esdm.co.uk/map1?version=1.3.0&amp;service=WMS&amp;request=GetLegendGraphic&amp;sld_version=1.1.0&amp;layer=grid&amp;format=image/png&amp;STYLE=default" title="GetLegendGraphic image" /></p>

<p>There you go Dennis, we have working OGC services from MapServer with nice clean URLs, a simple architecture for adding new services with their own map files, and no leaking of details about our directory structures.</p>

<p>&nbsp;</p>

<h3>Footnote…</h3>

<p>I’m not completely keen on the last bit, having to change the wms_onlineresource, as this means the original service is now broken (depending on whether the mapping client makes us of this, which QGIS likes to). Another way <em>should be</em>&nbsp; to create an outbound URL Rewrite rule to rewrite any references to the original URL with the new one. However, after a bit of pfaffing with this, I’m pretty sure there’s a bug in URL Rewrite that means outbound rewriting doesn’t work for attributes containing colons, and ours is “xlink:href”. Incidentally setting up the outbound rules for custom tags is a bit of an adventure through the UI, and there is a bit of escaping to do for the backslashes and question mark in the URL (<font style="background-color: rgb(255, 255, 0);">highlighted</font> below) as URL Rewrite is doing regular expressions so you might as well get down and dirty in the web.config located in the “dummy” folder we created for the new web site we created earlier. So this is my outbound rule that <em>doesn’t work,</em> but should when someone fixes the bug!</p>

<p><font face="Courier New">&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />
&lt;configuration&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;system.webServer&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;rewrite&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;rules&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;rule name="ReverseProxyInboundRule2" stopProcessing="true"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;match url="map1(.*)" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;action type="Rewrite" url="</font><a href="http://mapservertest.esdm.co.uk/scripts/mapserv.exe?map=D:\Websites\mapservertest\map\mapfiletest.map{R:1}&quot;"><font face="Courier New">http://mapservertest.esdm.co.uk/scripts/mapserv.exe?map=D:\Websites\mapservertest\map\mapfiletest.map{R:1}"</font></a><font face="Courier New"> /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/rule&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/rules&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;outboundRules&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;rule name="ReverseProxyOutboundRule1" preCondition="ResponseIsXML" enabled="true"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;match filterByTags="CustomTags" customTags="OGC GetCapabilities" pattern="^http(s)?://mapservertest.esdm.co.uk/scripts/mapserv.exe<font style="background-color: rgb(255, 255, 0);">\?map=D:\\Websites\\mapservertest\\map\\</font>mapfiletest.map(.*)" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;action type="Rewrite" value="http{R:1}://mapservertestwrapper.esdm.co.uk/map1{R:2}" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/rule&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;preConditions&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;preCondition name="ResponseIsHtml1"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/preCondition&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;preCondition name="ResponseIsXML"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/xml" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/preCondition&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/preConditions&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;customTags&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tags name="OGC GetCapabilities"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tag name="OnlineResource" attribute="<font style="background-color: rgb(255, 0, 0);">xlink:href</font>" /&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tags&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/customTags&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/outboundRules&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/rewrite&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/system.webServer&gt;<br />
&lt;/configuration&gt;</font></p>

<p>&nbsp;</p>

<p>The <font style="background-color: rgb(255, 0, 0);">red bit</font> is the bit affected by the bug I think, unless someone can point out how this should be escaped/expressed to make it work?</p>
<br /><a href='https://www.esdm.co.uk/how-to-hide-the-mapserver-map-file-location-on-iis'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/how-to-hide-the-mapserver-map-file-location-on-iis'>...</a>]]></description>
      <link>https://www.esdm.co.uk/how-to-hide-the-mapserver-map-file-location-on-iis</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/how-to-hide-the-mapserver-map-file-location-on-iis</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/how-to-hide-the-mapserver-map-file-location-on-iis</guid>
      <pubDate>Sat, 12 Apr 2014 11:29:00 GMT</pubDate>
    </item>
    <item>
      <title>Calling a secured WMS / WFS service from mapserver</title>
      <description><![CDATA[<p>Mapserver can be used in a cascading manner to call to other services, potentially combining them and maybe re-projecting before serving out again.</p>

<p>Reasons for this might include:</p>

<ul>
	<li>Re-projecting the map</li>
	<li>Accessing a secured WMS/WFS and then protecting it a different way</li>
	<li>Serving a WMS / WFS map out in a different way</li>
</ul>

<p>This is all relatively simple using standard mapserver .MAP file if the WMS / WFS services you are calling to are unsecured.&nbsp; Then its simply a case of a layer like:</p>

<blockquote>
<p>&nbsp;<br />
LAYER</p>

<p>NAME "MyLayer"<br />
DEBUG 5<br />
&nbsp; TYPE RASTER<br />
&nbsp; STATUS ON<br />
&nbsp; CONNECTION <a href="http://somedomain.com/WMSServer">http://somedomain.com/WMSServer</a><br />
&nbsp; CONNECTIONTYPE WMS<br />
&nbsp; METADATA<br />
&nbsp;&nbsp;&nbsp; "wms_title"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "OSI"<br />
&nbsp;&nbsp;&nbsp; "wms_srs"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "EPSG:27700 EPSG:4326 EPSG:54004 EPSG:3978 EPSG:3857 EPSG:900913"<br />
&nbsp;&nbsp;&nbsp; "wms_name"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "1,2,3,4,5,6,7,8,9"<br />
&nbsp;&nbsp;&nbsp; "wms_server_version"&nbsp; "1.1.1"<br />
&nbsp;&nbsp;&nbsp; "wms_format"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "image/png"<br />
&nbsp;&nbsp;&nbsp; "wms_enable_request"&nbsp; "*"<br />
&nbsp; END<br />
&nbsp; PROJECTION<br />
&nbsp;&nbsp;&nbsp;&nbsp; "init=epsg:900913"<br />
&nbsp; END<br />
&nbsp;<br />
END</p>
</blockquote>

<p>However if the service you are calling is secured it gets a whole load more complicated.&nbsp; You might (as I did initially) think that all you have to do is change the map file to include the authorisation details such as</p>

<blockquote>
<p>&nbsp;<br />
LAYER</p>

<p>NAME "MySecuredLayer"<br />
DEBUG 5<br />
&nbsp; TYPE RASTER<br />
&nbsp; STATUS ON<br />
&nbsp; CONNECTION <a href="https://somedomain.com/WMSServer">https://somedomain.com/WMSServer</a><br />
&nbsp; CONNECTIONTYPE WMS<br />
&nbsp; METADATA<br />
&nbsp;&nbsp;&nbsp; "wms_title"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "OSI"<br />
&nbsp;&nbsp;&nbsp; "wms_srs"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "EPSG:27700 EPSG:4326 EPSG:54004 EPSG:3978 EPSG:3857 EPSG:900913"<br />
&nbsp;&nbsp;&nbsp; "wms_name"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "1,2,3,4,5,6,7,8,9"<br />
&nbsp;&nbsp;&nbsp; "wms_server_version"&nbsp; "1.1.1"<br />
&nbsp;&nbsp;&nbsp; "wms_format"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "image/jpeg"<br />
&nbsp;&nbsp;&nbsp; "wms_auth_username"&nbsp;&nbsp; "myUserName"<br />
&nbsp;&nbsp;&nbsp; "wms_auth_password"&nbsp;&nbsp; "myPassword"&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; "wms_auth_type"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "basic"<br />
&nbsp;&nbsp;&nbsp; "wms_enable_request"&nbsp; "*"<br />
&nbsp; END<br />
&nbsp; PROJECTION<br />
&nbsp;&nbsp;&nbsp;&nbsp; "init=epsg:900913"<br />
&nbsp; END<br />
&nbsp;<br />
END</p>
</blockquote>

<p>Unfortunately that in itself doesn’t work (though the map file itself is correct).&nbsp; You are very strongly recommended to make sure you have DEBUG setup – at least while testing.&nbsp; So in addition to specifying the DEBUG level in the layer – you also need to include in the overall header of the MAP file a path to the log file</p>

<blockquote>
<p>CONFIG "MS_ERRORFILE" "C:\\Mapserver\\ms_log.txt"<br />
DEBUG 5</p>
</blockquote>

<p>Running the above map file will create errors in the log file:</p>

<p><strong><em>HTTP: request failed with curl error code 60 (SSL certificate problem, verify that the CA cert is OK. Details:<br />
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed) for </em></strong><a href="https://somedomain.com/WMSServer"><strong><em>https://somedomain.com/WMSServer</em></strong></a><strong><em>…</em></strong></p>

<p>(If you get an error:</p>

<p><strong>&nbsp;<em>msHTTPExecuteRequests(): HTTP request error. HTTP: request failed with curl error code 1 (Protocol https not supported or disabled in libcurl)</em></strong></p>

<p>Then you have a version of mapserver that is not compiled with the correct support and you need to change your version of mapserver (see below))</p>

<p>Basically the secure WMS call uses cURL libraries to do the call – and you have to register the certificate from the service you are calling.</p>

<h1>What you need to do:</h1>

<h2>Make sure your version of mapserver supports https</h2>

<p>Make sure you have a version of mapserver that supports https calls.&nbsp; Tamas builds (<a href="http://www.gisinternals.com/sdk/" title="http://www.gisinternals.com/sdk/">http://www.gisinternals.com/sdk/</a>) seem to include the required libraries. I used release-1500-gdal-1-10-0-mapserver-6-2-1.zip though I’m sure other versions would work as well</p>

<h2>Get the security certificate</h2>

<p>Use OpenSSL (whcih is included in the mapserver distribution in bin\curl)</p>

<blockquote>
<p>openssl s_client –connect somedomain:443 &gt; c:\Temp\cert.txt</p>
</blockquote>

<p>If all goes well – the cert.txt file will look something like this – and you need the bits from -----BEGIN CERTIFICATE-----&nbsp; to&nbsp; -----END CERTIFICATE-----</p>

<p>CONNECTED(00000190)<br />
---<br />
Certificate chain<br />
0 s:/OU=Domain Control Validated/OU=COMODO SSL Wildcard/CN=*.osi.ie<br />
&nbsp;&nbsp; i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO SSL CA<br />
1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO SSL CA<br />
&nbsp;&nbsp; i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root<br />
---<br />
Server certificate<br />
-----BEGIN CERTIFICATE-----<br />
MIIE5DCCA8ygAwIBAgIQOhI8AlhJBZcVfKqx4ruAnzANBgkqhkiG9w0BAQUFADBw<br />
.<br />
9WspBdI6j1t8l+ZZGe1PXBIBaHlpXuazvlC5FpU93bKt7zaEJ1iU5XjIwB/H4ys1<br />
Hsch6g7YXnw=<br />
-----END CERTIFICATE-----<br />
subject=/OU=Domain Control Validated/OU=COMODO SSL Wildcard/CN=*.osi.ie<br />
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO SSL CA<br />
---<br />
No client certificate CA names sent<br />
---<br />
SSL handshake has read 2675 bytes and written 536 bytes<br />
---<br />
New, TLSv1/SSLv3, Cipher is AES256-SHA<br />
Server public key is 2048 bit<br />
Secure Renegotiation IS NOT supported<br />
Compression: NONE<br />
Expansion: NONE<br />
SSL-Session:<br />
&nbsp;&nbsp;&nbsp; Protocol&nbsp; : TLSv1<br />
&nbsp;&nbsp;&nbsp; Cipher&nbsp;&nbsp;&nbsp; : AES256-SHA<br />
&nbsp;&nbsp;&nbsp; Session-ID: 5F65EE6F4E78B564620101A5A706E7A259375B2277F430BC4578395780D14458<br />
&nbsp;&nbsp;&nbsp; Session-ID-ctx:<br />
&nbsp;&nbsp;&nbsp; Master-Key: 1C992E7CC5DFE1AF7B2D87295954D6A73A740E71BD81A2DB68546E61C055D0089FF1C91FB761F64CFE1BBD20B8411AE3<br />
&nbsp;&nbsp;&nbsp; Key-Arg&nbsp;&nbsp; : None<br />
&nbsp;&nbsp;&nbsp; PSK identity: None<br />
&nbsp;&nbsp;&nbsp; PSK identity hint: None<br />
&nbsp;&nbsp;&nbsp; Start Time: 1391380021<br />
&nbsp;&nbsp;&nbsp; Timeout&nbsp;&nbsp; : 300 (sec)<br />
&nbsp;&nbsp;&nbsp; Verify return code: 20 (unable to get local issuer certificate)<br />
---</p>

<h2>Add the security certificate to your local store</h2>

<p>I copied the default file from bin\curl\curl-ca-bundle.crt into the main mapserver cgi-bin (or scripts) folder</p>

<p>Edit the curl-ca-bundle.crt file and add in your certificate (the bits from -----BEGIN CERTIFICATE-----&nbsp; to&nbsp; -----END CERTIFICATE-----)</p>

<h2>Test your certificate</h2>

<p>Check Curl.exe is in the same folder as your curl-ca-bundle.crt</p>

<p>1) I tested first with a call to getCapabilities:</p>

<blockquote>
<p>curl -u MyUsername:MyPassword&nbsp;<a href="https://SomeDomain.com:443/WMSServer?REQUEST=GetCapabilities%26SERVICE=WMS">https://SomeDomain.com:443/WMSServer?REQUEST=GetCapabilities%26SERVICE=WMS</a></p>
</blockquote>

<p>(Note I had to swap &amp; for %26 0 i.e. url encode)</p>

<p>If this has worked you should see a normal getCapabilities text response</p>

<p>2) I then tested with an image call:</p>

<blockquote>
<p>curl -u MyUsername:MyPassword&nbsp;<a href="https://SomeDomain.com:443/WMSServer?LAYERS=1%26REQUEST=GetMap%26SERVICE=WMS">https://SomeDomain.com:443/WMSServer?LAYERS=1%26REQUEST=GetMap%26SERVICE=WMS</a> ……</p>
</blockquote>

<p>If this works you should see a load of extended characters returned (i.e. an image displayed in the command window!)</p>

<h2>Set a windows environment variable to point at the certificate store file</h2>

<p>Previous tests have simply used the file in the same folder as the curl.exe, but when running for real mapserver must know where to find the certificate file.</p>

<p>Set the CURL_CA_BUNDLE environment variable to point at the certificate file</p>

<p>e.g. C:\Mapserver\cgi-bin\curl-ca-bundle.crt</p>

<p>Then you need to reboot the server to make sure the new environment setting is correctly used</p>

<h2>Then test your map file</h2>

<p>Now – at last – the map file which you thought should work originally should actually work and return you an image.</p>

<blockquote>
<p>&nbsp;<br />
LAYER</p>

<p>NAME "MySecuredLayer"<br />
DEBUG 5<br />
&nbsp; TYPE RASTER<br />
&nbsp; STATUS ON<br />
&nbsp; CONNECTION <a href="https://somedomain.com/WMSServer">https://somedomain.com/WMSServer</a><br />
&nbsp; CONNECTIONTYPE WMS<br />
&nbsp; METADATA<br />
&nbsp;&nbsp;&nbsp; "wms_title"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "OSI"<br />
&nbsp;&nbsp;&nbsp; "wms_srs"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "EPSG:27700 EPSG:4326 EPSG:54004 EPSG:3978 EPSG:3857 EPSG:900913"<br />
&nbsp;&nbsp;&nbsp; "wms_name"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "1,2,3,4,5,6,7,8,9"<br />
&nbsp;&nbsp;&nbsp; "wms_server_version"&nbsp; "1.1.1"<br />
&nbsp;&nbsp;&nbsp; "wms_format"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "image/jpeg"<br />
&nbsp;&nbsp;&nbsp; "wms_auth_username"&nbsp;&nbsp; "myUserName"<br />
&nbsp;&nbsp;&nbsp; "wms_auth_password"&nbsp;&nbsp; "myPassword"&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; "wms_auth_type"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "basic"<br />
&nbsp;&nbsp;&nbsp; "wms_enable_request"&nbsp; "*"<br />
&nbsp; END<br />
&nbsp; PROJECTION<br />
&nbsp;&nbsp;&nbsp;&nbsp; "init=epsg:900913"<br />
&nbsp; END<br />
&nbsp;<br />
END</p>
</blockquote>

<p>If you look in the log file you should see:</p>

<blockquote>
<p>Using CURL_CA_BUNDLE=C:\Mapserver\cgi-bin\curl-ca-bundle.crt</p>
</blockquote>

<p>If not – check your environment variable setting</p>

<h2>Other references</h2>

<p>The above was put together from a variety of sources – including those below– useful additional refs include:</p>

<p><a href="http://geographika.co.uk/setting-up-a-secure-cascading-wms-on-mapserver" title="http://geographika.co.uk/setting-up-a-secure-cascading-wms-on-mapserver">http://geographika.co.uk/setting-up-a-secure-cascading-wms-on-mapserver</a></p>

<p><a href="http://www.mapserver.org/ogc/wxs_secure.html" title="http://www.mapserver.org/ogc/wxs_secure.html">http://www.mapserver.org/ogc/wxs_secure.html</a></p>

<p><a href="http://trac.osgeo.org/mapserver/ticket/3070" title="http://trac.osgeo.org/mapserver/ticket/3070">http://trac.osgeo.org/mapserver/ticket/3070</a></p>
<br /><a href='https://www.esdm.co.uk/calling-a-secured-wms--wfs-service-from-mapserver'></a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/calling-a-secured-wms--wfs-service-from-mapserver'>...</a>]]></description>
      <link>https://www.esdm.co.uk/calling-a-secured-wms--wfs-service-from-mapserver</link>
      <author>()</author>
      <comments>https://www.esdm.co.uk/calling-a-secured-wms--wfs-service-from-mapserver</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/calling-a-secured-wms--wfs-service-from-mapserver</guid>
      <pubDate>Mon, 03 Feb 2014 23:31:00 GMT</pubDate>
    </item>
    <item>
      <title>Using R with spatial data</title>
      <description><![CDATA[<p>&nbsp;</p> <p>R is a cross platform statistical package which is becoming extremely widely used.&nbsp; It is modular and so there are all sorts of add ins available, including a number of sophisticated tools for spatial analysis some of which run considerably faster than Arc / MapInfo.&nbsp; Organisations are also starting to move to this for spatial analysis as its cheaper but more relevantly doesn’t suffer from the endless version changes that ArcGIS is particular forces on people.</p> <p>R is generally used for a wide range of statistics – but also has the option to generate publication quality graphs (pdfs or images)</p> <h1>Getting started</h1> <p>R can be downloaded here: <a href="http://www.r-project.org/">http://www.r-project.org/</a></p> <p>Its also suggested (on Windows) to use r studio - <a href="http://www.rstudio.com/">http://www.rstudio.com/</a> which gives a more friendly IDE</p> <p>R is a scripted tool – so it can be a steep learning curve but there are endless examples on the web to help get started.</p> <h1>Spatial example using OGR</h1> <p>This syntax is a little obscure – but potentially of considerable use for a wide range of spatial data.&nbsp; Reading from shapefiles (or any other OGR supported datasource is easy / possible)</p> <h2>Read data from SQL server</h2> <p>#download and load rgdal libraries<br>install.packages("rgdal")<br>library(rgdal)</p> <p>#Use ogrinfo to get information about a shape file called stations<br>ogrInfo("C:/ToFile/RNLI/data", "Stations")</p> <p>#List the spatial layers available in a SQL server database<br>#Note you must have the standard OGR tables geometry_columns and spatial_ref_sys present and populated in your database<br>#Some people say you need a DSN as well – not sure you do …<br>ogrListLayers("MSSQL:server=SQL2008;database=CMSI-NE;trusted_connection=yes") </p> <p>#Read a table of polygon objects into a new sitesPoly object<br>#Then plot them on a map<br>#N.B.&nbsp; You can’t have binary data in your table – so if you do create a view or somesuch<br>sitesPoly &lt;- readOGR(dsn='MSSQL:server=SQL2008;database=CMSI-NE;trusted_connection=true', layer='SitesGeom') <br>plot(sitesPoly) </p> <p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_84.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_thumb_70.png" width="244" height="171"></a></p> <p>#Select just one record from the layer<br>oneSite = subset(sitesPoly, sitesPoly$MI_PRINX == 420)</p> <p>#Make sure the layer is assigned the correct projection<br>proj4string(oneSite) = CRS("+init=epsg:27700")</p> <p>#Transform the layer to another projection<br>oneSite_latlong = spTransform(oneSite, CRS("+init=epsg:4326"))</p> <p>#Load another library which contains some world boundaries<br>#Load up coast and countries outlines<br>library(rworldmap)<br>data(coastsCoarse)<br>data(countriesLow)</p><pre>&nbsp;</pre>
<p>#plot countries outline limiting extent to Europe<br>plot(countriesLow, xlim = c(-10, 10), ylim = c(45, 60), legend = F)</p>
<p>#Add sites onto existing map setting colours</p>
<p>proj4string(sitesPoly_ll) = CRS("+init=epsg:27700")<br>sitesPoly_ll = spTransform(sitesPoly, CRS("+init=epsg:4326"))<br>plot(sitesPoly_ll, col = "red", border="red", Add=T)</p>
<p>&nbsp;</p>
<h2>Use a different library to do some plotting</h2>
<p>install.packages("maps")<br>install.packages("mapdata")<br>library(maps)<br>library(mapsdata)</p>
<p>#Select polygons making up UK and plot<br>#Then plot a point on top<br>map('worldHires',<br>&nbsp;&nbsp;&nbsp; c('UK', 'Ireland', 'Isle of Man','Isle of Wight','Wales:Anglesey'),<br>&nbsp;&nbsp;&nbsp; xlim=c(-11,3), ylim=c(49,60.9))<br>points(-1.615672,54.977768,col=2,pch=18)</p>
<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_85.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_thumb_71.png" width="244" height="170"></a></p><br /><a href='https://www.esdm.co.uk/using-r-with-spatial-data'></a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/using-r-with-spatial-data'>...</a>]]></description>
      <link>https://www.esdm.co.uk/using-r-with-spatial-data</link>
      <author>()</author>
      <comments>https://www.esdm.co.uk/using-r-with-spatial-data</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/using-r-with-spatial-data</guid>
      <pubDate>Sat, 28 Dec 2013 12:53:39 GMT</pubDate>
    </item>
    <item>
      <title>How to set up a MapInfo DBMS table from SQL Server without a DSN</title>
      <description><![CDATA[<p>A quick note to say that it is definitely possible to&nbsp;use a SQL Server spatial table in MapInfo Professional without an ODBC&nbsp;DSN. There's not much about this on the web, and the normal methods of opening SQL Server tables&nbsp;all seem to want a DSN. Here's a resulting .TAB file (this assumes you have the table or view configured in the MAPINFO.MAPINFO_MAPCATALOG table):</p>

<p>&nbsp;</p>

<p><code>!table<br />
!version 1000<br />
!charset WindowsLatin1</code></p>

<p><code>Definition Table<br />
&nbsp; Type ODBC<br />
begin_metadata<br />
"\IsReadOnly" = "FALSE"<br />
"\MapInfo" = ""<br />
"\MapInfo\TableID" = "2e88f9e5-7d4c-4645-b824-4ef6b5102c9e"<br />
"\DATALINK" = ""<br />
"\DATALINK\Query" = "Select *&nbsp; From ""myDatabase"".""dbo"".""myTableOrView"""<br />
"\DATALINK\ConnectionString" = "Description=aDescription;DRIVER=SQL Server;SERVER=myServer;Trusted_Connection=Yes;APP=MapInfo Professional®;WSID=blah;DATABASE=myDatabase"<br />
"\DATALINK\ToolKit" = "ODBC"<br />
"\CACHE" = "OFF"<br />
"\MBRSEARCH" = "ON"<br />
end_metadata</code></p>
<br /><a href='https://www.esdm.co.uk/how-to-set-up-a-mapinfo-dbms-table-from-sql-server-without-a-dsn'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/how-to-set-up-a-mapinfo-dbms-table-from-sql-server-without-a-dsn'>...</a>]]></description>
      <link>https://www.esdm.co.uk/how-to-set-up-a-mapinfo-dbms-table-from-sql-server-without-a-dsn</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/how-to-set-up-a-mapinfo-dbms-table-from-sql-server-without-a-dsn</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/how-to-set-up-a-mapinfo-dbms-table-from-sql-server-without-a-dsn</guid>
      <pubDate>Thu, 14 Nov 2013 18:41:00 GMT</pubDate>
    </item>
    <item>
      <title>Optimising SQL Server spatial indexing - let sql do the work..</title>
      <description><![CDATA[<p>Note – this will take a LONG time to run – quite possibly many hours - on a big dataset as it drops and recreates the index using different parameters – sledgehammer approach, but given the permutations available in SQL server spatial indexing and the level of confusion around optimising – this is the empirical approach.</p>

<p>lifted from here: <a href="http://stackoverflow.com/questions/2920948/selecting-a-good-sql-server-2008-spatial-index-with-large-polygons">http://stackoverflow.com/questions/2920948/selecting-a-good-sql-server-2008-spatial-index-with-large-polygons</a></p>

<p>Basically crate the SPROC listed at the bottom, then setup some example calls (3 or 4 different ones to minimise caching artefacts)</p>

<p>Then set it running and go on holiday …</p>

<h2>Setup your parameters:</h2>

<p>DECLARE @g1 VARCHAR(MAX)<br />
SET @g1 = 'GEOMETRY::STGeomFromText(''POLYGON((252783.989267352 461095.507712082,260134.010732648 461095.507712082,260134.010732648 465068.492287918,252783.989267352 465068.492287918,252783.989267352 461095.507712082))'', 28992)'<br />
DECLARE @g2 VARCHAR(MAX)<br />
SET @g2 = 'GEOMETRY::STGeomFromText(''POLYGON((5580.146375 5340.100667,655613.186375 5340.100667,655613.186375 1219811.501845,5580.146375 1219811.501845,5580.146375 5340.100667))'',28992)'<br />
exec sp_tune_spatial_index 'OBSVEG.OBS_Locations' ,'sidx_OBS_Locations_geom',2,8,@g1,@g2</p>

<h2>Create Stored procedure:</h2>

<p><em>N.B. Assumes geometry field is called geom</em><br />
<em>N.B. You MUST modify the SPROC to use extents for your SRID</em></p>

<pre class="csharpcode">
<span class="kwrd">USE</span> [CMSI-OBSVEG]
<span class="kwrd">GO</span>
/****** <span class="kwrd">Object</span>:  StoredProcedure [dbo].[sp_tune_spatial_index]    Script <span class="kwrd">Date</span>: 10/01/2013 11:35:27 ******/
<span class="kwrd">SET</span> ANSI_NULLS <span class="kwrd">ON</span>
<span class="kwrd">GO</span>
<span class="kwrd">SET</span> QUOTED_IDENTIFIER <span class="kwrd">ON</span>
<span class="kwrd">GO</span>

<span class="kwrd">ALTER</span> <span class="kwrd">PROCEDURE</span> [dbo].[sp_tune_spatial_index]
(
  @tabnm                <span class="kwrd">VARCHAR</span>(<span class="kwrd">MAX</span>),    <span class="rem">-- This parameter stores the name of the spatial table for which you are tuning the index</span>
    @idxnm                <span class="kwrd">VARCHAR</span>(<span class="kwrd">MAX</span>),    <span class="rem">-- This parameter stores the name of the spatial index of the named table</span>
    @min_cells_per_obj    <span class="kwrd">INT</span>,            <span class="rem">-- Minimum Cells Per Object to test on. Suggested to start at 2.</span>
    @max_cells_per_obj    <span class="kwrd">INT</span>,            <span class="rem">-- Maximum Cells Per Object to test on.</span>
    
    /*    The test requires two geometry instances <span class="kwrd">to</span> <span class="kwrd">use</span> <span class="kwrd">in</span> test query 1 <span class="kwrd">and</span> 2.
        The <span class="kwrd">first</span> one should cover the area <span class="kwrd">of</span> <span class="kwrd">default</span> extent. The <span class="kwrd">second</span> should
        cover an area roughly the <span class="kwrd">size</span> <span class="kwrd">of</span> the area shown <span class="kwrd">when</span> zoomed <span class="kwrd">in</span>, panning
        around. It <span class="kwrd">is</span> required that the <span class="kwrd">variable</span> store a string that will <span class="kwrd">create</span>
        the geometry instance since this will be done within the <span class="kwrd">procedure</span> <span class="kwrd">and</span> 
        cannot be a <span class="kwrd">variable</span> <span class="kwrd">of</span> type: GEOMETRY. The SRID <span class="kwrd">of</span> these instances must
        <span class="kwrd">match</span> that <span class="kwrd">of</span> the <span class="kwrd">table</span> you <span class="kwrd">are</span> testing. */
    @testgeom1            <span class="kwrd">VARCHAR</span>(<span class="kwrd">MAX</span>),    <span class="rem">-- This parameter stores the first geometry instance creation string that will be used in the test</span>
    @testgeom2            <span class="kwrd">VARCHAR</span>(<span class="kwrd">MAX</span>)    <span class="rem">-- This parameter stores the second geometry instance creation string that will be used in the test</span>
    
)

<span class="kwrd">AS</span>

<span class="kwrd">SET</span> NOCOUNT <span class="kwrd">ON</span>;

/*    <span class="kwrd">Prior</span> <span class="kwrd">to</span> running this <span class="kwrd">procedure</span>, two tables <span class="kwrd">are</span> required. These tables <span class="kwrd">are</span> 
    created here <span class="kwrd">to</span> <span class="kwrd">prepare</span> <span class="kwrd">for</span> running the <span class="kwrd">procedure</span>.    */

<span class="kwrd">PRINT</span> <span class="str">'Checking for required tables...'</span>
<span class="kwrd">IF</span> <span class="kwrd">EXISTS</span>(<span class="kwrd">SELECT</span> 1 <span class="kwrd">FROM</span> sysobjects <span class="kwrd">WHERE</span> name <span class="kwrd">IN</span> (<span class="str">'cell_opt_perm'</span>, <span class="str">'spat_idx_test_result'</span>))
    <span class="kwrd">BEGIN</span>
        <span class="kwrd">PRINT</span> <span class="str">'... The "cell_opt_perm" and "spat_idx_test_result" tables exist.'</span>
    <span class="kwrd">END</span>
<span class="kwrd">ELSE</span>
<span class="kwrd">BEGIN</span>
    <span class="kwrd">PRINT</span> <span class="str">'... Creating "cell_opt_perm" and "spat_idx_test_result" tables.'</span>
    <span class="kwrd">CREATE</span> <span class="kwrd">TABLE</span> cell_opt_perm(
        [perm_id] [<span class="kwrd">smallint</span>] <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>,
        [permutation] [nvarchar](4) <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>,
        [level1] [nvarchar](6) <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>,
        [level2] [nvarchar](6) <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>,
        [level3] [nvarchar](6) <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>,
        [level4] [nvarchar](6) <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>
    )

    INSERT <span class="kwrd">INTO</span> cell_opt_perm ([perm_id], [permutation], [level1], [level2], [level3], [level4])
    <span class="kwrd">VALUES</span> (1,<span class="str">'LLLL'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>),
        (2,<span class="str">'LLLM'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>),
        (3,<span class="str">'LLLH'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>),
        (4,<span class="str">'LLML'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>),
        (5,<span class="str">'LLMM'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>),
        (6,<span class="str">'LLMH'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>),
        (7,<span class="str">'LLHL'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>),
        (8,<span class="str">'LLHM'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>),
        (9,<span class="str">'LLHH'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>),
        (10,<span class="str">'LMLL'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>),
        (11,<span class="str">'LMLM'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>),
        (12,<span class="str">'LMLH'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>),
        (13,<span class="str">'LMML'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>),
        (14,<span class="str">'LMMM'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>),
        (15,<span class="str">'LMMH'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>),
        (16,<span class="str">'LMHL'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>),
        (17,<span class="str">'LMHM'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>),
        (18,<span class="str">'LMHH'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>),
        (19,<span class="str">'LHLL'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>),
        (20,<span class="str">'LHLM'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>),
        (21,<span class="str">'LHLH'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>),
        (22,<span class="str">'LHML'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>),
        (23,<span class="str">'LHMM'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>),
        (24,<span class="str">'LHMH'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>),
        (25,<span class="str">'LHHL'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>),
        (26,<span class="str">'LHHM'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>),
        (27,<span class="str">'LHHH'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>),
        (28,<span class="str">'MLLL'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>),
        (29,<span class="str">'MLLM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>),
        (30,<span class="str">'MLLH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>),
        (31,<span class="str">'MLML'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>),
        (32,<span class="str">'MLMM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>),
        (33,<span class="str">'MLMH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>),
        (34,<span class="str">'MLHL'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>),
        (35,<span class="str">'MLHM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>),
        (36,<span class="str">'MLHH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>),
        (37,<span class="str">'MMLL'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>),
        (38,<span class="str">'MMLM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>),
        (39,<span class="str">'MMLH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>),
        (40,<span class="str">'MMML'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>),
        (41,<span class="str">'MMMM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>),
        (42,<span class="str">'MMMH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>),
        (43,<span class="str">'MMHL'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>),
        (44,<span class="str">'MMHM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>),
        (45,<span class="str">'MMHH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>),
        (46,<span class="str">'MHLL'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>),
        (47,<span class="str">'MHLM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>),
        (48,<span class="str">'MHLH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>),
        (49,<span class="str">'MHML'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>),
        (50,<span class="str">'MHMM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>),
        (51,<span class="str">'MHMH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>),
        (52,<span class="str">'MHHL'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>),
        (53,<span class="str">'MHHM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>),
        (54,<span class="str">'MHHH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>),
        (55,<span class="str">'HLLL'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>),
        (56,<span class="str">'HLLM'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>),
        (57,<span class="str">'HLLH'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>),
        (58,<span class="str">'HLML'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>),
        (59,<span class="str">'HLMM'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>),
        (60,<span class="str">'HLMH'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>),
        (61,<span class="str">'HLHL'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>),
        (62,<span class="str">'HLHM'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>),
        (63,<span class="str">'HLHH'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>),
        (64,<span class="str">'HMLL'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>),
        (65,<span class="str">'HMLM'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>),
        (66,<span class="str">'HMLH'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>),
        (67,<span class="str">'HMML'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>),
        (68,<span class="str">'HMMM'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>),
        (69,<span class="str">'HMMH'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>),
        (70,<span class="str">'HMHL'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>),
        (71,<span class="str">'HMHM'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>),
        (72,<span class="str">'HMHH'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>),
        (73,<span class="str">'HHLL'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'LOW'</span>),
        (74,<span class="str">'HHLM'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'MEDIUM'</span>),
        (75,<span class="str">'HHLH'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>,<span class="str">'HIGH'</span>),
        (76,<span class="str">'HHML'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'LOW'</span>),
        (77,<span class="str">'HHMM'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'MEDIUM'</span>),
        (78,<span class="str">'HHMH'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>,<span class="str">'HIGH'</span>),
        (79,<span class="str">'HHHL'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'LOW'</span>),
        (80,<span class="str">'HHHM'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'MEDIUM'</span>),
        (81,<span class="str">'HHHH'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>,<span class="str">'HIGH'</span>)
    
    <span class="kwrd">CREATE</span> <span class="kwrd">TABLE</span> spat_idx_test_result(
        [perm_id] [<span class="kwrd">int</span>] <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>,
        [num_cells] [<span class="kwrd">int</span>] <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>,
        [permut] [nvarchar](4) <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>,
        [g1t1] [bigint] <span class="kwrd">NULL</span>,
        [g1t2] [bigint] <span class="kwrd">NULL</span>,
        [g1t3] [bigint] <span class="kwrd">NULL</span>,
        [g1t4] [bigint] <span class="kwrd">NULL</span>,
        [g2t1] [bigint] <span class="kwrd">NULL</span>,
        [g2t2] [bigint] <span class="kwrd">NULL</span>,
        [g2t3] [bigint] <span class="kwrd">NULL</span>,
        [g2t4] [bigint] <span class="kwrd">NULL</span>
    )
    
    INSERT <span class="kwrd">INTO</span> dbo.spat_idx_test_result
    <span class="kwrd">VALUES</span> (0,16,0,<span class="kwrd">NULL</span>,<span class="kwrd">NULL</span>,<span class="kwrd">NULL</span>,<span class="kwrd">NULL</span>,<span class="kwrd">NULL</span>,<span class="kwrd">NULL</span>,<span class="kwrd">NULL</span>,<span class="kwrd">NULL</span>)
<span class="kwrd">END</span>


/*    <span class="kwrd">delete</span> <span class="kwrd">all</span> <span class="kwrd">rows</span> <span class="kwrd">from</span> "spat_idx_test_result" <span class="kwrd">table</span>. This makes it ready <span class="kwrd">to</span> stuff <span class="kwrd">in</span> <span class="kwrd">new</span> results.
    !!!WARNING!!! <span class="kwrd">if</span> your test was interupted, the <span class="kwrd">table</span> will be cleared <span class="kwrd">out</span> <span class="kwrd">and</span> the test will
    <span class="kwrd">begin</span> <span class="kwrd">from</span> the beginning. You could try <span class="kwrd">to</span> <span class="kwrd">modify</span> this <span class="kwrd">to</span> <span class="kwrd">start</span> <span class="kwrd">where</span> you <span class="kwrd">left</span> <span class="kwrd">off</span> but
    I didn<span class="str">'t have time and this worked well enough for me. */
DELETE FROM spat_idx_test_result
    WHERE perm_id != 0

/* set up counters */
DECLARE @a1 INT
DECLARE @a2 INT
DECLARE @a3 INT
DECLARE @a4 INT

/* set up variables to hold high/medium/low values and permutation to use in rebuilding
   the spatial index and recording stats */
DECLARE @lev1 VARCHAR(6)
DECLARE @lev2 VARCHAR(6)
DECLARE @lev3 VARCHAR(6)
DECLARE @lev4 VARCHAR(6)
DECLARE @permut VARCHAR(6)
DECLARE @num_cell VARCHAR(4)
DECLARE @time_str VARCHAR(20)
DECLARE @perm_id VARCHAR(20)

/* create variables to hold timestamps for beginning and ending of test queries */
DECLARE @start_t DATETIME
DECLARE @end_t DATETIME
DECLARE @elapse_t INT

/* begin looping through cell option permutations */
SET @a1 = @min_cells_per_obj
WHILE @a1 &lt;= @max_cells_per_obj
    BEGIN
        SET @a2 = 1
        PRINT '</span>Started Testing <span class="kwrd">for</span> <span class="str">' +CAST(@a1 AS VARCHAR(10)) +'</span> cells per <span class="kwrd">object</span><span class="str">'
        WHILE @a2 &lt; 82
            BEGIN
                SELECT @lev1 = level1, @lev2 = level2, @lev3 = level3, @lev4 = level4 FROM cell_opt_perm WHERE perm_id = @a2
                SET @permut = '</span><span class="str">''</span><span class="str">' +(SELECT permutation FROM cell_opt_perm WHERE perm_id = @a2) +'</span><span class="str">''</span><span class="str">'
                EXEC
                    ('</span>
                        <span class="kwrd">CREATE</span> SPATIAL <span class="kwrd">INDEX</span> <span class="str">' +@idxnm +'</span> <span class="kwrd">ON</span> <span class="str">' +@tabnm +'</span> 
                        (
                            [geom]
                        )
                        <span class="kwrd">USING</span>  GEOMETRY_GRID 
                        <span class="kwrd">WITH</span>
                            (
                                BOUNDING_BOX =(-100, -100, 300000, 650000),
                                GRIDS =(LEVEL_1 = <span class="str">' +@lev1 +'</span> ,LEVEL_2 = <span class="str">' +@lev2 +'</span> ,LEVEL_3 = <span class="str">' +@lev3 +'</span> ,LEVEL_4 = <span class="str">' +@lev4 +'</span> ), 
                                CELLS_PER_OBJECT = <span class="str">' +@a1 +'</span> ,
                                PAD_INDEX  = <span class="kwrd">OFF</span>,
                                SORT_IN_TEMPDB = <span class="kwrd">OFF</span>,
                                DROP_EXISTING = <span class="kwrd">ON</span>,
                                ALLOW_ROW_LOCKS  = <span class="kwrd">ON</span>,
                                ALLOW_PAGE_LOCKS  = <span class="kwrd">ON</span>,
                                <span class="kwrd">FILLFACTOR</span> = 100
                            )
                        <span class="kwrd">ON</span> [<span class="kwrd">PRIMARY</span>]<span class="str">'
                    )
                PRINT '</span>Re-built <span class="kwrd">index</span> <span class="kwrd">to</span> <span class="str">' +@permut
                SET @a3 = 1
                SET @a4 = 1
                WHILE @a3 &lt; 5
                    BEGIN
                        SET @start_t = GETDATE()
                        EXEC
                            (
                                '</span><span class="kwrd">CREATE</span> <span class="kwrd">TABLE</span> #tmp_tab (shp GEOMETRY)
                                <span class="kwrd">DECLARE</span> @g1 GEOMETRY
                                <span class="kwrd">SET</span> @g1 = <span class="str">' +@testgeom1 +'</span>
                                INSERT #tmp_tab (shp)
                                    <span class="kwrd">SELECT</span>
                                        r.geom <span class="kwrd">AS</span> shp
                                    <span class="kwrd">FROM</span>
                                        <span class="str">' +@tabnm +'</span> r
                                    <span class="kwrd">WHERE</span>
                                        r.geom.STIntersects(@g1) = 1
                                <span class="kwrd">DROP</span> <span class="kwrd">TABLE</span> #tmp_tab<span class="str">'
                            )
                        SET @end_t = GETDATE()
                        SET @elapse_t = (SELECT DATEDIFF(MS, @start_t, @end_t))
                        SET @num_cell = CAST(@a1 AS VARCHAR(6))
                        SET @time_str = CAST(@elapse_t AS VARCHAR(20))
                        IF @a3 = 1
                            BEGIN
                                IF (SELECT TOP 1 perm_id FROM spat_idx_test_result) IS NULL
                                    BEGIN
                                        SET @perm_id = 1
                                    END
                                ELSE
                                    BEGIN
                                        SET @perm_id = CAST((SELECT MAX(perm_id+1) FROM spat_idx_test_result) AS VARCHAR(20))
                                    END
                                EXEC
                                    (
                                        '</span>INSERT <span class="kwrd">INTO</span> spat_idx_test_result (perm_id, num_cells, permut, g1t<span class="str">' +@a3 +'</span>)
                                        <span class="kwrd">VALUES</span> (<span class="str">' +@perm_id +'</span>, <span class="str">' +@num_cell +'</span>, <span class="str">' +@permut +'</span>, <span class="str">' +@time_str +'</span>)<span class="str">'
                                    )
                            END
                        ELSE
                            EXEC
                                (
                                    '</span><span class="kwrd">UPDATE</span> spat_idx_test_result
                                    <span class="kwrd">SET</span>
                                    num_cells = <span class="str">' +@num_cell +'</span>,
                                    permut = <span class="str">' +@permut +'</span>,
                                    g1t<span class="str">' +@a3 +'</span> = <span class="str">' +@time_str +'</span>
                                    <span class="kwrd">WHERE</span> perm_id = <span class="str">' +@perm_id
                                )
                        SET @a3 = @a3 + 1
                    END
                WHILE @a4 &lt; 5
                    BEGIN
                        SET @start_t = GETDATE()
                        EXEC
                            (
                                '</span><span class="kwrd">CREATE</span> <span class="kwrd">TABLE</span> #tmp_tab (shp GEOMETRY) 
                                <span class="kwrd">DECLARE</span> @g2 GEOMETRY
                                <span class="kwrd">SET</span> @g2 = <span class="str">' +@testgeom2 +'</span>
                                INSERT #tmp_tab (shp)
                                    <span class="kwrd">SELECT</span>
                                        r.geom <span class="kwrd">AS</span> shp
                                    <span class="kwrd">FROM</span>
                                        <span class="str">' +@tabnm +'</span> r
                                    <span class="kwrd">WHERE</span>
                                        r.geom.STIntersects(@g2) = 1
                                <span class="kwrd">DROP</span> <span class="kwrd">TABLE</span> #tmp_tab<span class="str">'
                            )
                        SET @end_t = GETDATE()
                        SET @elapse_t = (SELECT DATEDIFF(MS, @start_t, @end_t))
                        SET @num_cell = CAST(@a1 AS VARCHAR(6))
                        SET @time_str = CAST(@elapse_t AS VARCHAR(20))
                        EXEC
                            (
                                '</span><span class="kwrd">UPDATE</span> spat_idx_test_result
                                <span class="kwrd">SET</span>
                                num_cells = <span class="str">' +@num_cell +'</span>,
                                permut = <span class="str">' +@permut +'</span>,
                                g2t<span class="str">' +@a4 +'</span> = <span class="str">' +@time_str +'</span>
                                <span class="kwrd">WHERE</span> perm_id = <span class="str">' +@perm_id
                            )
                        SET @a4 = @a4 + 1
                    END
                SET @a2 = @a2 + 1
            END
        SET @a1 = @a1 + 1
    END
PRINT '</span>Testing <span class="kwrd">of</span> <span class="str">' +@tabnm +'</span> spatial <span class="kwrd">index</span>: <span class="str">' +@idxnm +'</span> <span class="kwrd">is</span> complete!'
</pre>
<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_58.png"><img alt="image" border="0" height="170" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_thumb_47.png" style="margin: 0px; border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="image" width="244" /></a></p>
<br /><a href='https://www.esdm.co.uk/optimising-sql-server-spatial-indexing-let-sql-do-the-work'></a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/optimising-sql-server-spatial-indexing-let-sql-do-the-work'>...</a>]]></description>
      <link>https://www.esdm.co.uk/optimising-sql-server-spatial-indexing-let-sql-do-the-work</link>
      <author>()</author>
      <comments>https://www.esdm.co.uk/optimising-sql-server-spatial-indexing-let-sql-do-the-work</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/optimising-sql-server-spatial-indexing-let-sql-do-the-work</guid>
      <pubDate>Tue, 01 Oct 2013 09:11:00 GMT</pubDate>
    </item>
    <item>
      <title>When GeoServer forgets about GeoWebCache</title>
      <description><![CDATA[<p>I’ve been setting up some new mapping services (WMS) in GeoServer, with GeoWebCache allowing the map tiles to be cached for performance.&nbsp; For this to work, and for the WMS to use its cache of tiles, requires …</p> <ol> <li>the WMS request should include this on the KVP URL: &amp;tiled=true</li> <li>the client should construct its tile requests so that they match the server tiles – this is not the place for elaboration, but essentially this means getting the origin and tile size correct</li> <li>the GeoServer must have “Enable direct integration with GeoServer WMS” switched on. </li></ol> <p>The setting in #3 is found under Tile Caching &gt; Caching Defaults in the main GeoServer menu. Set it once, and forget it. Or so I thought.</p> <p>How do we know if the WMS is using its cache? Insect the http requests in Fiddler (or similar) and check the headers of the responses. </p> <p>If the WMS is using its cache you will see lines in the headers like this</p> <p><font face="Courier New">geowebcache-cache-result: HIT</font></p> <p>along with various other geowebcache-… values</p> <p>If it knows it has a cache, but something is wrong with the WMS requests (item #2 above), you might see headers like this:</p> <p><font face="Courier New">geowebcache-miss-reason: request does not align to grid(s) 'OSGB27700'<br>geowebcache-cache-result: MISS</font></p> <p>And if the WMS is unaware it even has a cache of tiles at all, you will see nothing about geowebcache at all in the headers. And of course, poor WMS performance may give you a clue something is not working as you hoped. </p> <p>Well I had set everything up lovely and my cache was working well, giving super fast performance of my Ordnance Survey mapping once the initial requests had populated the cache for an area.</p> <p><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="fast Ordnance Survey maps" border="0" alt="fast Ordnance Survey maps" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_36.png" width="640" height="315"></p> <p>But the very next day… performance was rubbish, and on inspecting the http headers, it turned out the WMS had forgotten about its cache.</p> <p>In the GeoServer admin interface, the “Enable direct integration with GeoServer WMS” was now set to false. Grrr – I wondered how I could stupidly have switched that off again. Ah well, switch it on again and move on. </p> <p>A few hours later… same again. This time I started suspecting colleagues, but there was no evidence, or motive, yet…</p> <p>A couple more times, and I realised it was reverting after any re-start of GeoServer. I had been restarting every now and then, to make sure changed wrapper log settings etc. stuck, but it turned out this had been un-sticking my essential cache setting.</p> <p>The solution turned out to be to edit the gwc-gs.xml file, found in the GeoServer data directory, and set this:</p> <p><font face="Courier New">&nbsp; &lt;directWMSIntegrationEnabled&gt;true&lt;/directWMSIntegrationEnabled&gt;</font></p> <p>In my data dir, there was a gwc-gs.xml.tmp file, and I can only assume GeoServer had always failed to update the main XML file for some reason, used the .tmp version for the duration of the session, then reverted to the main one upon a re-start.</p> <p>Now my cached WMS layers run nice and fast, and GeoServer doesn’t forget the setting.</p><br /><a href='https://www.esdm.co.uk/when-geoserver-forgets-about-geowebcache'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/when-geoserver-forgets-about-geowebcache'>...</a>]]></description>
      <link>https://www.esdm.co.uk/when-geoserver-forgets-about-geowebcache</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/when-geoserver-forgets-about-geowebcache</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/when-geoserver-forgets-about-geowebcache</guid>
      <pubDate>Tue, 05 Mar 2013 20:12:27 GMT</pubDate>
    </item>
    <item>
      <title>British National Grid in MapInfo</title>
      <description><![CDATA[<p>
	A problem frequently encountered with sharing data between MapInfo and ArcGIS is that the British National Grid projection used by ArcGIS is accurate to 1 mm, whilst the standard projection used by MapInfo is not. This can be an issue, as boundary data that matches Ordnance Survey MasterMap data in MapInfo will appear to be poorly mapped (albeit by a just a few cm) when compared with MasterMap data in ArcGIS.</p>
<p>
	In order to resolve this, MapInfo introduced a 1 mm accuracy projection, which can be found under <em>Projection... </em>when you create a table or save a copy of an existing table. However, this is not a default option, so even when saving a copy of data already in the 1 mm precision projection MapInfo will automatically default to the standard projection. This makes it very easy to accidentally reduce the quality of your data if you forget to actively select the 1 mm precision projection.</p>
<p>
	Fortunately there is a relatively simple way to ensure that MapInfo always defaults to the 1 mm projection:</p>
<ul>
	<li>
		Navigate to the MapInfo folder under program files (e.g. C:\Program Files (x86)\MapInfo\Professional).</li>
	<li>
		Locate the MAPINFOW.PRJ file.<img alt="The MapInfo Choose Projection dialogue, altered so that the British National Grid projection defaults to 1 mm accuracy" src="https://www.esdm.co.uk/Data/Sites/1/media/knowledgebase/gisknowledge/alteredchooseprojectiondialogue.png" style="width: 447px; height: 365px; float: right;" title="Altered MapInfo Choose Projection dialogue" /></li>
	<li>
		Backup the MAPINFOW.PRJ file, in case you make a mistake.</li>
	<li>
		Open the MAPINFOW.PRJ file in a text editor (e.g. notepad).</li>
	<li>
		Search for ‘British’, which will find a section of text that looks like this:</li>
</ul>
<p style="margin-left: 80px;">
	"--- British Coordinate Systems ---"<br />
	"British National Grid\p27700", 8, 79, 7, -2, 49, 0.9996012717, 400000, -100000<br />
	"British National Grid (1 mm accuracy)", 2008, 79, 7, -2, 49,0.9996012717, 400000, -100000, 0, 0, 2000000, 2000000<br />
	"British OSGRS80 Grid", 8, 33, 7, -2, 49, 0.9996012717, 400000, -100000</p>
<ul>
	<li>
		Move the third line of this text up one so that it becomes the second:</li>
</ul>
<p style="margin-left: 80px;">
	"--- British Coordinate Systems ---"<br />
	"British National Grid (1 mm accuracy)", 2008, 79, 7, -2, 49,0.9996012717, 400000, -100000, 0, 0, 2000000, 2000000<br />
	"British National Grid\p27700", 8, 79, 7, -2, 49, 0.9996012717, 400000, -100000<br />
	"British OSGRS80 Grid", 8, 33, 7, -2, 49, 0.9996012717, 400000, -100000</p>
<p>
	This change moves the 1 mm accuracy projection to the top of the <em>Category Members </em>selector under the British Coordinate Systems <em>Category </em>in the <em>Choose Projection </em>dialogue box of MapInfo, automatically making it the default option (as shown in the picture). If you want to use the standard projection then you just need to select 'British National Grid [EPSG: 27700]' from the dialogue instead.</p>
<p>
	&nbsp;</p>
<br /><a href='https://www.esdm.co.uk/british-national-grid-in-mapinfo'>Mike Lush</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/british-national-grid-in-mapinfo'>...</a>]]></description>
      <link>https://www.esdm.co.uk/british-national-grid-in-mapinfo</link>
      <author>mike.lush@idoxgroup.com (Mike Lush)</author>
      <comments>https://www.esdm.co.uk/british-national-grid-in-mapinfo</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/british-national-grid-in-mapinfo</guid>
      <pubDate>Wed, 28 Nov 2012 11:36:00 GMT</pubDate>
    </item>
    <item>
      <title>Calculating the extent of the geometry in a SQL Server table</title>
      <description><![CDATA[<p>Andy Brewer passed me this, adapted from <a href="http://barendgehrels.blogspot.co.uk/2011/04/extent-of-sql-server-spatial-table.html">http://barendgehrels.blogspot.co.uk/2011/04/extent-of-sql-server-spatial-table.html</a> <p>This is a useful script, using CTEs (common table expressions) to calculate the extent of the geometry in a SQL Server table. Replace “geom” with the name of your geometry column and “MyTable” with the name of your table. <p>Note that this script depends on STEnvelope returning a rectangular extent with its first point at the SW corner and its third in the NE corner. Tests from the author of the blog above confirm the SQL Server currently does this.<pre>with
  cte_envelope as
  (
    select geom.STEnvelope() as envelope from MyTable
  ),
  cte_corner as
  (
    select envelope.STPointN(1) as point from cte_envelope
    union all
    select envelope.STPointN(3) from cte_envelope
  )
select min(point.STX) as min_x, min(point.STY) as min_y, max(point.STX) as max_x, max(point.STY) as max_y
from cte_corner</pre><br /><a href='https://www.esdm.co.uk/calculating-the-extent-of-the-geometry-in-a-sql-server-table'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/calculating-the-extent-of-the-geometry-in-a-sql-server-table'>...</a>]]></description>
      <link>https://www.esdm.co.uk/calculating-the-extent-of-the-geometry-in-a-sql-server-table</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/calculating-the-extent-of-the-geometry-in-a-sql-server-table</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/calculating-the-extent-of-the-geometry-in-a-sql-server-table</guid>
      <pubDate>Tue, 20 Mar 2012 19:18:00 GMT</pubDate>
    </item>
    <item>
      <title>MapServer and GeoServer (and tilecache) comparison serving Ordnance Survey raster maps</title>
      <description><![CDATA[<p>
	With two WMS running off identical data on the same server, I thought it would be interesting to compare speeds and the map output. So I lined up a map request with identical parameters to both services and ran them through Fiddler a few times (to get an idea of the response times).</p>
<h3>
	Output quality and performance</h3>
<table border="1" cellpadding="2" cellspacing="0" width="600">
	<tbody>
		<tr>
			<td valign="top" width="200">
				&nbsp;</td>
			<td valign="top" width="200">
				<strong>GeoServer</strong></td>
			<td valign="top" width="200">
				<strong>MapServer</strong></td>
		</tr>
		<tr>
			<td valign="top" width="200">
				Response time</td>
			<td valign="top" width="200">
				0.8 to 1.0 seconds with USE_JAI_IMAGEREAD=true<br />
				0.6 to 1.3 seconds with<br />
				USE_JAI_IMAGEREAD=false</td>
			<td valign="top" width="200">
				0.4 to 0.6 seconds</td>
		</tr>
		<tr>
			<td valign="top" width="200">
				Image size</td>
			<td valign="top" width="200">
				63,574 bytes</td>
			<td valign="top" width="200">
				78,327 bytes</td>
		</tr>
		<tr>
			<td valign="top" width="200">
				The map</td>
			<td valign="top" width="200">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/esdm_uk_basemaps-50krastercolour%5B4%5D.png"><img alt="ESDM_UK_BaseMaps-50KRasterColour[4]" border="0" height="516" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/esdm_uk_basemaps-50krastercolour%5B4%5D_thumb.png" style="border-width: 0px; margin: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="ESDM_UK_BaseMaps-50KRasterColour[4]" width="276" /></a></td>
			<td valign="top" width="200">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/mapserv%5B4%5D.png"><img alt="mapserv[4]" border="0" height="516" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/mapserv%5B4%5D_thumb.png" style="border-width: 0px; margin: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="mapserv[4]" width="276" /></a></td>
		</tr>
		<tr>
			<td valign="top" width="200">
				Quality</td>
			<td valign="top" width="200">
				Adequate, but quite fractured</td>
			<td valign="top" width="200">
				Lovely</td>
		</tr>
		<tr>
			<td valign="top" width="200">
				Comments</td>
			<td valign="top" width="200">
				A significantly slower response than MapServer, and a worse image.<br />
				I couldn’t find any difference between the different re-sampling methods, perhaps suggesting that the settings were not taking effect (I’ve seen GeoServer GUI issues like this a couple of times now, requiring diving into the XML config files). Later – yes the settings were present in the XML config, but seem to make no difference to the output.</td>
			<td valign="top" width="200">
				Faster and much nicer image.<br />
				If we turn off bilinear re-sampling, then the response time improves to around 0.3 seconds and the image deteriorates slightly as shown below (becoming similar in quality to the GeoServer map).<br />
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/mapservca53l4zs_thumb%5B3%5D.png"><img alt="mapservCA53L4ZS_thumb[3]" border="0" height="516" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/mapservca53l4zs_thumb%5B3%5D_thumb.png" style="border-width: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="mapservCA53L4ZS_thumb[3]" width="276" /></a></td>
		</tr>
	</tbody>
</table>
<p>
	Oh dear this is not looking good for GeoServer. But is it the whole story? Next I compared them in a more realistic scenario, as base maps in OpenLayers, trying both single tile and multi-tile modes. I configured the map to view the 1:50,000 mapping at a scale where no re-sampling could occur.</p>
<table border="1" cellpadding="2" cellspacing="0" width="671">
	<tbody>
		<tr>
			<td valign="top" width="256">
				&nbsp;</td>
			<td valign="top" width="144">
				<strong>GeoServer</strong></td>
			<td valign="top" width="269">
				<strong>MapServer</strong></td>
		</tr>
		<tr>
			<td valign="top" width="256">
				Response time multi-tile (30 tiles each 256x256 pixels)</td>
			<td valign="top" width="144">
				0.2 to 1.2 seconds, but on average a shade faster</td>
			<td valign="top" width="269">
				0.3 to 0.7 seconds</td>
		</tr>
		<tr>
			<td valign="top" width="256">
				Response time single-tile (1725 x 1173 pixels)</td>
			<td valign="top" width="144">
				4 to 6 seconds</td>
			<td valign="top" width="269">
				3 to 5 seconds</td>
		</tr>
		<tr>
			<td valign="top" width="256">
				Image size (typical 256x256 tile)</td>
			<td valign="top" width="144">
				30,447 bytes</td>
			<td valign="top" width="269">
				22,674 seconds</td>
		</tr>
		<tr>
			<td valign="top" width="256">
				Image size (1725 x 1173 pixels)</td>
			<td valign="top" width="144">
				646,681 bytes</td>
			<td valign="top" width="269">
				690,494 bytes</td>
		</tr>
		<tr>
			<td valign="top" width="256">
				The map (256x256 tile)</td>
			<td valign="top" width="144">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/geoserv_256tile.png"><img alt="geoserv_256tile" border="0" height="260" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/geoserv_256tile_thumb.png" style="border-width: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="geoserv_256tile" width="260" /></a></td>
			<td valign="top" width="269">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/mapserv_256tile.png"><img alt="mapserv_256tile" border="0" height="260" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/mapserv_256tile_thumb.png" style="border-width: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="mapserv_256tile" width="260" /></a></td>
		</tr>
		<tr>
			<td valign="top" width="256">
				Quality</td>
			<td valign="top" width="144">
				Very good</td>
			<td valign="top" width="269">
				Very good (pretty much identical)</td>
		</tr>
	</tbody>
</table>
<p>
	OK so now it is looking better for GeoServer; when there is no re-sampling it can return identical images to MapServer, though slightly larger, and performance is pretty similar and possibly quicker.</p>
<h3>
	Output quality and performance when re-projecting</h3>
<p>
	Now time to see how they cope with serving the maps into a different coordinate system, and in particular the global spherical Mercator used in Google and Bing Maps.</p>
<table border="1" cellpadding="2" cellspacing="0" width="671">
	<tbody>
		<tr>
			<td valign="top" width="256">
				&nbsp;</td>
			<td valign="top" width="144">
				<strong>GeoServer</strong></td>
			<td valign="top" width="269">
				<strong>MapServer</strong></td>
		</tr>
		<tr>
			<td valign="top" width="256">
				Response time multi-tile (30 tiles each 256x256 pixels)</td>
			<td valign="top" width="144">
				0.2 to 1.2 seconds</td>
			<td valign="top" width="269">
				0.1 to 0.5 seconds</td>
		</tr>
		<tr>
			<td valign="top" width="256">
				Response time single-tile (1725 x 1173 pixels)</td>
			<td valign="top" width="144">
				10 seconds</td>
			<td valign="top" width="269">
				6 seconds</td>
		</tr>
		<tr>
			<td valign="top" width="256">
				Image size (typical 256x256 tile)</td>
			<td valign="top" width="144">
				29,169 bytes</td>
			<td valign="top" width="269">
				37,261 seconds</td>
		</tr>
		<tr>
			<td valign="top" width="256">
				Image size (1725 x 1173 pixels)</td>
			<td valign="top" width="144">
				1,041,098 bytes</td>
			<td valign="top" width="269">
				982,050 bytes</td>
		</tr>
		<tr>
			<td valign="top" width="256">
				The map (256x256 tile)</td>
			<td valign="top" width="144">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/geoserv_900913.png"><img alt="geoserv_900913" border="0" height="260" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/geoserv_900913_thumb.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="geoserv_900913" width="260" /></a></td>
			<td valign="top" width="269">
				<p>
					<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/mapserv_900913.png"><img alt="mapserv_900913" border="0" height="260" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/mapserv_900913_thumb.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="mapserv_900913" width="260" /></a></p>
			</td>
		</tr>
		<tr>
			<td valign="top" width="256">
				Quality</td>
			<td valign="top" width="144">
				Pretty horrible</td>
			<td valign="top" width="269">
				Very good</td>
		</tr>
	</tbody>
</table>
<p>
	With default settings, GeoServer produces horrible output when re-projecting the raster maps. But this does need more investigation when I found out how to make it use different re-sampling algorithms.</p>
<h3>
	Performance under load</h3>
<p>
	Now time to see how they perform under load using multi-mechanize. I chose to compare MapServer, GeoServer and a tilecache at returning a 256x256 tile in EPSG:900913 (i.e. re-projecting on-the-fly). Each test ran for 100 seconds, starting with 10 virtual users, increasing by 10 every 10 seconds. The tilecache image was of course cached and simply being retrieved from disk. All tests were using the same web server, with files on the same drive.</p>
<p>
	<em>Note: the vertical scale differs in each graph to fit the observed values.</em></p>
<table border="1" cellpadding="2" cellspacing="0" width="600">
	<tbody>
		<tr>
			<td valign="top" width="150">
				&nbsp;</td>
			<td valign="top" width="150">
				<strong>GeoServer</strong></td>
			<td valign="top" width="150">
				<strong>MapServer</strong></td>
			<td valign="top" width="150">
				<strong>Tilecache</strong></td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Transactions</td>
			<td valign="top" width="150">
				381</td>
			<td valign="top" width="150">
				556</td>
			<td valign="top" width="150">
				2188</td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Errors</td>
			<td valign="top" width="150">
				57</td>
			<td valign="top" width="150">
				0</td>
			<td valign="top" width="150">
				0</td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Average response time</td>
			<td valign="top" width="150">
				6.1</td>
			<td valign="top" width="150">
				6.5</td>
			<td valign="top" width="150">
				2.3</td>
		</tr>
		<tr>
			<td valign="top" width="150">
				95% response time</td>
			<td valign="top" width="150">
				26.8</td>
			<td valign="top" width="150">
				17.6</td>
			<td valign="top" width="150">
				5.1</td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Response graph</td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times.png"><img alt="All_Transactions_response_times" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_thumb.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_response_times" width="244" /></a></td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_1.png"><img alt="All_Transactions_response_times" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_thumb_1.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_response_times" width="244" /></a></td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_2.png"><img alt="All_Transactions_response_times" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_thumb_2.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_response_times" width="244" /></a></td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Transactions per second</td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput.png"><img alt="All_Transactions_throughput" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_thumb.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_throughput" width="244" /></a></td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_1.png"><img alt="All_Transactions_throughput" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_thumb_1.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_throughput" width="244" /></a></td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_2.png"><img alt="All_Transactions_throughput" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_thumb_2.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_throughput" width="244" /></a></td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Comments</td>
			<td valign="top" width="150">
				Completely broken after about 80 seconds – had to re-start GeoServer</td>
			<td valign="top" width="150">
				No errors, but significantly disrupted performance</td>
			<td valign="top" width="150">
				Slight slow-down under high load, but very reassuring performance</td>
		</tr>
	</tbody>
</table>
<p>
	GeoServer clearly could not stand up to more than about 30 virtual users when re-projecting the maps, and ultimately the service stalled completely requiring a re-start.</p>
<p>
	I tried the tests again requesting the maps in OSGB so that the service did not involve re-projection. I also reduced the number of virtual users to 50. The results were quite surprising…</p>
<table border="1" cellpadding="2" cellspacing="0" width="602">
	<tbody>
		<tr>
			<td valign="top" width="150">
				&nbsp;</td>
			<td valign="top" width="150">
				<strong>GeoServer</strong></td>
			<td valign="top" width="150">
				<strong>MapServer</strong></td>
			<td valign="top" width="150">
				<strong>Tilecache</strong></td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Transactions</td>
			<td valign="top" width="150">
				2018</td>
			<td valign="top" width="150">
				1114</td>
			<td valign="top" width="150">
				2197</td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Errors</td>
			<td valign="top" width="150">
				0</td>
			<td valign="top" width="150">
				0</td>
			<td valign="top" width="150">
				0</td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Average response time</td>
			<td valign="top" width="150">
				1.3</td>
			<td valign="top" width="150">
				2.4</td>
			<td valign="top" width="150">
				1.1</td>
		</tr>
		<tr>
			<td valign="top" width="150">
				95% response time</td>
			<td valign="top" width="150">
				2.4</td>
			<td valign="top" width="150">
				9.1</td>
			<td valign="top" width="150">
				2.5</td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Response graph</td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_3.png"><img alt="All_Transactions_response_times" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_thumb_3.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_response_times" width="244" /></a></td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_4.png"><img alt="All_Transactions_response_times" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_thumb_4.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_response_times" width="244" /></a></td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_5.png"><img alt="All_Transactions_response_times" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_thumb_5.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_response_times" width="244" /></a></td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Transactions per second</td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_3.png"><img alt="All_Transactions_throughput" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_thumb_3.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_throughput" width="244" /></a></td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_4.png"><img alt="All_Transactions_throughput" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_thumb_4.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_throughput" width="244" /></a></td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_5.png"><img alt="All_Transactions_throughput" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_thumb_5.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_throughput" width="244" /></a></td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Comments</td>
			<td valign="top" width="150">
				Impeccable!</td>
			<td valign="top" width="150">
				Much better than when re-projecting, but only half as good as GeoServer.</td>
			<td valign="top" width="150">
				Only fractionally better than GeoServer</td>
		</tr>
	</tbody>
</table>
<p>
	This is a hugely impressive result for GeoServer. My guess is that it is employing server-side in-memory caching, and because all my requests were identical the responses were very fast. MapServer on the other hand has to start a CGI process of each request and presumably cannot benefit from an in-memory cache.</p>
<p>
	When re-projecting on the other hand, perhaps it does not use a cache. And because all the GeoServer operations are running as one process, it is restricted to one virtual processor on our server whereas each MapServer exe can grab one for itself. Certainly when running these tests the server was running at about 40-80% CPU with MapServer, compared to 7% with GeoServer.</p>
<p>
	I ran this test again with the number of virtual users doubled back to 100, and GeoServer came through almost unscathed, this time processing 2072 transactions though with 3 errors.</p>
<table border="1" cellpadding="2" cellspacing="0" width="602">
	<tbody>
		<tr>
			<td valign="top" width="150">
				&nbsp;</td>
			<td valign="top" width="150">
				<strong>GeoServer</strong></td>
			<td valign="top" width="150">
				<strong>MapServer</strong></td>
			<td valign="top" width="150">
				<strong>Tilecache</strong></td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Transactions</td>
			<td valign="top" width="150">
				2072</td>
			<td valign="top" width="150">
				907</td>
			<td valign="top" width="150">
				2234</td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Errors</td>
			<td valign="top" width="150">
				3</td>
			<td valign="top" width="150">
				0</td>
			<td valign="top" width="150">
				0</td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Average response time</td>
			<td valign="top" width="150">
				2.4</td>
			<td valign="top" width="150">
				5.9</td>
			<td valign="top" width="150">
				2.3</td>
		</tr>
		<tr>
			<td valign="top" width="150">
				95% response time</td>
			<td valign="top" width="150">
				5.9</td>
			<td valign="top" width="150">
				25.6</td>
			<td valign="top" width="150">
				4.8</td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Response graph</td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_6.png"><img alt="All_Transactions_response_times" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_thumb_6.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_response_times" width="244" /></a></td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_7.png"><img alt="All_Transactions_response_times" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_thumb_7.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_response_times" width="244" /></a></td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_8.png"><img alt="All_Transactions_response_times" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_response_times_thumb_8.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_response_times" width="244" /></a></td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Transactions per second</td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_6.png"><img alt="All_Transactions_throughput" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_thumb_6.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_throughput" width="244" /></a></td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_7.png"><img alt="All_Transactions_throughput" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_thumb_7.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_throughput" width="244" /></a></td>
			<td valign="top" width="150">
				<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_8.png"><img alt="All_Transactions_throughput" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/all_transactions_throughput_thumb_8.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="All_Transactions_throughput" width="244" /></a></td>
		</tr>
		<tr>
			<td valign="top" width="150">
				Comments</td>
			<td valign="top" width="150">
				Wow</td>
			<td valign="top" width="150">
				Suffering, but no errors</td>
			<td valign="top" width="150">
				The best as expected</td>
		</tr>
	</tbody>
</table>
<p>
	I suspect this test may not predict real-world behaviour with lots of users requesting different WMS maps, where I suspect GeoServer would lose its advantage, but it is interesting anyway.</p>
<p>
	NB I had GeoServer running with AllowMultithreading=true</p>
<h3>
	Summary</h3>
<p>
	If the maps need to be re-projected then use MapServer – both performance and output quality are vastly superior. In a site that is going to get any significant load it is unwise to re-project raster mapping on-the-fly anyway, and it should definitely be cached. In this case the&nbsp;WMS performance doesn’t matter so much but output quality is paramount, so MapServer it is.&nbsp; If I find a way of making GeoServer produce nice looking output I’ll come back and revise this post!</p>
<p>
	If no re-projection is required, then simple observation has the two roughly equal under light load. The maps looked almost identical, and performance was close. GeoServer had the advantage for me in that it didn’t suffer from MapServer’s tendency to choke on some requests; in practice this meant I went over my entire OSGB cache again with a GeoServer WMS as the source, to plug the gaps left by the MapServer service.</p>
<p>
	Under heavy (albeit contrived) load, the crucial factor was whether or not there was any re-projection involved. Without re-projection GeoServer produced astonishing performance, thrashing MapServer and nearly matching a tilecache. Introduce re-projection however, and GeoServer collapses to the extent that it can crash entirely – not good when it is running all your mapping services. MapServer never broke, and despite some gaps and slow responses, it managed to struggle through all tests without a single error.</p>
<p>
	So out of this experience I think we need to be using both, depending on the purpose.</p>
<p>
	Next to test performance on vector layers, including file based GIS formats and spatial databases…</p>
<br /><a href='https://www.esdm.co.uk/mapserver-and-geoserver-and-tilecache-comparison-serving-ordnance-survey-raster-maps'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/mapserver-and-geoserver-and-tilecache-comparison-serving-ordnance-survey-raster-maps'>...</a>]]></description>
      <link>https://www.esdm.co.uk/mapserver-and-geoserver-and-tilecache-comparison-serving-ordnance-survey-raster-maps</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/mapserver-and-geoserver-and-tilecache-comparison-serving-ordnance-survey-raster-maps</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/mapserver-and-geoserver-and-tilecache-comparison-serving-ordnance-survey-raster-maps</guid>
      <pubDate>Sat, 21 Jan 2012 17:53:00 GMT</pubDate>
    </item>
    <item>
      <title>Serving Ordnance Survey rasters with GeoServer and MapServer - some irks and quirks</title>
      <description><![CDATA[<p>
	I’ve been preparing a <a href="http://tilecache.org/" target="_blank" title="http://tilecache.org/">tilecache</a> of UK Ordnance Survey mapping for a couple of big projects, combining Open Data with licensed Landranger (1:50,000) and Explorer (1:25,000) mapping. The tilecache utility consumes and generates its cache from a WMS of the mapping, and fir this I’ve done most of the work using MapServer. However I’ve hit some stubborn problems with a few areas of mapping, for example this area where I have a blank strip enclosing a tile boundary. The original tiles are fine, but for some reason when render by MapServer we get this blank area.</p>
<p>
	<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_3.png"><img alt="Blank strip in Ordnance Survey 50K WMS from MapServer" border="0" height="204" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_thumb_4.png" style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Blank strip in Ordnance Survey 50K WMS from MapServer" width="244" /></a></p>
<p>
	In another couple of areas, the WMS requests to MapServer simply failed with this error sequence:</p>
<p>
	<font face="Courier New" size="1">[Sat Jan 21 09:30:02 2012].785000 msDrawRasterLayerLow(OS50KColourRaster): entering.<br />
	[Sat Jan 21 09:30:02 2012].791000 msResampleGDALToMap in effect: cellsize = 5.000000<br />
	[Sat Jan 21 09:30:02 2012].792000 msDrawGDAL(OS50KColourRaster): using RAW_WINDOW=3509 0 491 652, dst=0,0,491,652<br />
	[Sat Jan 21 09:30:02 2012].792000 msDrawGDAL(): red,green,blue,alpha bands = 1,0,0,0<br />
	[Sat Jan 21 09:30:02 2012].891000 msResampleGDALToMap in effect: cellsize = 5.000000<br />
	[Sat Jan 21 09:30:02 2012].892000 msDrawGDAL(OS50KColourRaster): using RAW_WINDOW=3509 3348 491 652, dst=0,0,491,652<br />
	[Sat Jan 21 09:30:02 2012].892000 msDrawGDAL(): red,green,blue,alpha bands = 1,0,0,0<br />
	[Sat Jan 21 09:30:02 2012].998000 msResampleGDALToMap in effect: cellsize = 5.000000<br />
	[Sat Jan 21 09:30:02 2012].998000 msDrawGDAL(OS50KColourRaster): using RAW_WINDOW=0 0 812 652, dst=0,0,812,652<br />
	[Sat Jan 21 09:30:02 2012].998000 msDrawGDAL(): red,green,blue,alpha bands = 1,0,0,0<br />
	[Sat Jan 21 09:30:03 2012].122000 msResampleGDALToMap in effect: cellsize = 5.000000<br />
	[Sat Jan 21 09:30:03 2012].122000 msDrawGDAL(OS50KColourRaster): using RAW_WINDOW=0 3348 812 652, dst=0,0,812,652<br />
	[Sat Jan 21 09:30:03 2012].122000 msDrawGDAL(): red,green,blue,alpha bands = 1,0,0,0<br />
	[Sat Jan 21 09:30:03 2012].148000 GetBlockRef failed at X block offset 0, Y block offset 499: Unable to access file. GDALDatasetRasterIO() failed: drawGDAL()<br />
	[Sat Jan 21 09:30:03 2012].149000 msDrawMap(): Image handling error. Failed to draw layer named 'OS50KColourRaster'.<br />
	[Sat Jan 21 09:30:03 2012].149000 freeLayer(): freeing layer at 024265B0.</font></p>
<p>
	again the underlying images were apparently fine.</p>
<p>
	And of course this has left gaps in the tile cache.</p>
<p>
	So, in order to overcome these issues I am setting up the WMS using GeoServer to see if that can fill the gaps.</p>
<p>
	My starting point is a folder containing the TIFF images, each with a .TFW world file.</p>
<p>
	Gotchas:</p>
<p>
	There are <a href="http://docs.geoserver.org/stable/en/user/tutorials/image_mosaic_plugin/imagemosaic.html" target="_blank" title="instructions here">instructions for setting up a raster WMS in GeoServer here</a>, but here is my summary with details of where things can go wrong:</p>
<ul>
	<li>
		Create a workspace, in my case “ESDM_UK_BaseMaps” with a namespace of “<a href="https://www.ordnancesurvey.co.uk/" title="https://www.ordnancesurvey.co.uk/">https://www.ordnancesurvey.co.uk/</a>”</li>
	<li>
		Create a store – this will equate with one type of map, in this case the Landranger 1:50,000 colour raster.</li>
	<li>
		Choose ImageMosaic as the type of store</li>
	<li>
		Select the workspace, and give the store a name, in this case “OS50K”</li>
	<li>
		For the connection parameters, enter the folder that contains the images, in my case “file:D:\MapData\OrdnanceSurvey\50KRasterColour”</li>
	<li>
		GeoServer will then automatically scan the folder and create a shapefile index for the tiles (or “granules” as they bizarrely call them). At this point I was confronted with this error message “Could not list layers for this store, an error occurred retrieving them: Unable to acquire a reader for this coverage with format: ImageMosaic”.</li>
</ul>
<p>
	This is a commonly observed error by all accounts with lots of fairly grumpy exchanges among GeoServer users and developers about how to reproduce it and what might be the causes. It seems it can be raised by a raft of different issues. So I returned to a close reading of the instructions and also found <a href="http://www.adrianwalker.org/2010/08/using-ordnance-survey-open-data-street.html" target="_blank" title="Adrian Walker’s blog">Adrian Walker’s useful blog</a> post about configuring OS maps in GeoServer.</p>
<p>
	It turns out I had two problems:</p>
<p>
	First the .TFW extension must be lower case. So in a command prompt run</p>
<p>
	<font face="Courier New" size="2">ren *.TFW *.tfw</font></p>
<p>
	(note that the image file extension does not have to be lower case)</p>
<p>
	Second, GeoServer needs a .prj file for every image (not required by MapServer).</p>
<p>
	These can be generated using a python script (thanks to Adrian Walker again for this tip):</p>
<pre class="csharpcode">
import glob
content = <span class="str">'PROJCS["OSGB 1936 / British National Grid", GEOGCS["OSGB 1936", DATUM["OSGB_1936", SPHEROID["Airy 1830",6377563.396,299.3249646, AUTHORITY["EPSG","7001"]], AUTHORITY["EPSG","6277"]], PRIMEM["Greenwich",0, AUTHORITY["EPSG","8901"]], UNIT["degree",0.01745329251994328, AUTHORITY["EPSG","9122"]], AUTHORITY["EPSG","4277"]], UNIT["metre",1, AUTHORITY["EPSG","9001"]], PROJECTION["Transverse_Mercator"], PARAMETER["latitude_of_origin",49], PARAMETER["central_meridian",-2], PARAMETER["scale_factor",0.9996012717], PARAMETER["false_easting",400000], PARAMETER["false_northing",-100000], AUTHORITY["EPSG","27700"], AXIS["Easting",EAST], AXIS["Northing",NORTH]]'</span>
tifs = glob.glob(<span class="str">'*.tif'</span>) 
<span class="kwrd">for</span> tif <span class="kwrd">in</span> tifs:  
    prj = tif.split(<span class="str">'.'</span>)[0] + <span class="str">'.prj'</span>  
    file = open(prj,<span class="str">'w'</span>)  
    file.writelines(content)  
    file.close()</pre>
<p>
	<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }	</style>
</p>
<p>
	After resolving these issues, it was necessary to return to step 2 above to create the store, but I got the error again! OK, GeoServer had created the index shapefile already, and it was bad due to the missing prj and tfw files; unfortunately GeoServer will not overwrite this file so I had to manually delete the files (the shapefile is named after the store, and comprises seven files with various extensions).&nbsp; After that, creating the store worked fine.</p>
<p>
	Hint: if you examine the .prj file in a text editor and it looks like this, then you need to bin it and start again:</p>
<p>
	<font face="Courier New" size="2">LOCAL_CS["Generic cartesian 2D",<br />
	LOCAL_DATUM["Unknow", 0],<br />
	UNIT["m", 1.0],<br />
	AXIS["x", EAST],<br />
	AXIS["y", NORTH]]</font></p>
<p>
	So on clicking “Save” for the new store, it showed a twirly for a couple of minutes while creating the index shapefile, and then transfers you to the New Layer page, showing the new layer that can be published (it names the layer after the folder containing the images). From there it is a simple matter to complete the configuration of the layer.</p>
<p>
	Note: I think I could have configured a single store one folder higher up, and this would have included each subfolder of TIFFs as a separate layer.</p>
<h3>
	Outcome</h3>
<p>
	Once I had finally got my GeoServer WMS up and running it was time to see whether could fills the gaps in my tilecache…. and I’m very pleased to say yes it could!</p>
<p>
	<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_4.png"><img alt="1:50K OS map from GeoServer, with no gap" border="0" height="204" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_thumb_5.png" style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="1:50K OS map from GeoServer, with no gap" width="244" /></a></p>
<p>
	I’ve posted separately about performance and image quality from the two servers.</p>
<br /><a href='https://www.esdm.co.uk/ordnance-survey-raster-with-geoserver-and-mapserver-some-irks-and-quirks'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/ordnance-survey-raster-with-geoserver-and-mapserver-some-irks-and-quirks'>...</a>]]></description>
      <link>https://www.esdm.co.uk/ordnance-survey-raster-with-geoserver-and-mapserver-some-irks-and-quirks</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/ordnance-survey-raster-with-geoserver-and-mapserver-some-irks-and-quirks</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/ordnance-survey-raster-with-geoserver-and-mapserver-some-irks-and-quirks</guid>
      <pubDate>Sat, 21 Jan 2012 09:46:00 GMT</pubDate>
    </item>
    <item>
      <title>AGI Cymru Conference 2011: HBSMR Mobile presentation</title>
      <description><![CDATA[<p>
	Held at Cardiff City Hall at the end of November, the <a href="http://www.agi.org.uk/cymru/">AGI Cymru</a> winter conference was attended by around 80 people and had a fair mix of managers and developers from different organizations around Wales. The mobile stream proved to be quite interesting with three presentations from ESDM, Cardiff Uni and <a href="http://www.mapaction.org">MapAction</a>.</p>
<p>
	Laine Skinner from ESDM presented a paper about a mobile application we have written for our HBSMR product, which is used by Historic Environment Records to manage heritage inventories and to support casework.</p>
<p>
	The presentation described how we created a mobile application over a short-targeted project in January 2011. The project itself was completed in a RAD/agile manner and highlights a successful way of working with a technology which itself is also undergoing huge change every few months.</p>
<p>
	The presentation is available online <a href="http://www.esdmdata.co.uk/Data/Sites/1/media/knowledgebase/gisknowledge/mobilegis/hbsmr_mobileproject/presentation/esdm_agicymru_winterconf2011_mobilegis.pdf.pdf">here</a>.</p>
<br /><a href='https://www.esdm.co.uk/agi-cymru-conference-2011-hbsmr-mobile-presentation'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/agi-cymru-conference-2011-hbsmr-mobile-presentation'>...</a>]]></description>
      <link>https://www.esdm.co.uk/agi-cymru-conference-2011-hbsmr-mobile-presentation</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/agi-cymru-conference-2011-hbsmr-mobile-presentation</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/agi-cymru-conference-2011-hbsmr-mobile-presentation</guid>
      <pubDate>Fri, 13 Jan 2012 18:54:00 GMT</pubDate>
    </item>
    <item>
      <title>AGI Cymru Conference 2011: GIS in the cloud</title>
      <description><![CDATA[<p>
	Laine Skinner from ESDM presented under the ‘New Directions’ theme at the AGI Cymru Conference. Held at Cardiff City Hall at the end of November, the <a href="http://www.agi.org.uk/cymru/">AGI Cymru</a> winter conference was attended by around 80 people and had a fair mix of managers and developers from different organizations around Wales.</p>
<p>
	Unfortunately there is no online content from the conference, although speaker details are available from the AGI and speakers are usually more than happy to discuss their work and forward details onward.</p>
<p>
	Laine’s presentation was titled:</p>
<blockquote>
	<p>
		GIS Cloud: Money for old rope or a new direction?</p>
</blockquote>
<p>
	Its available as a prezi (cloud based presentation service) <a href="http://prezi.com/ufmanknrsy3l/present/?auth_key=sbzfx6d&amp;follow=otbtwqp1smf1">here</a>. The presentation provides a short description about what the ‘GIS Cloud’ is and why it might be useful to a whole range of organizations. We discuss some practical uses of a real cloud implementation on <a href="http://aws.amazon.com">Amazons AWS product</a>.</p>
<br /><a href='https://www.esdm.co.uk/agi-cymru-conference-2011-gis-in-the-cloud'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/agi-cymru-conference-2011-gis-in-the-cloud'>...</a>]]></description>
      <link>https://www.esdm.co.uk/agi-cymru-conference-2011-gis-in-the-cloud</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/agi-cymru-conference-2011-gis-in-the-cloud</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/agi-cymru-conference-2011-gis-in-the-cloud</guid>
      <pubDate>Fri, 13 Jan 2012 18:47:00 GMT</pubDate>
    </item>
    <item>
      <title>Some jottings on SQL Azure and GIS</title>
      <description><![CDATA[<p>I have set up a SQL Azure 90 day trial (as of 18th November) to test whether ArcSquirrel can work with Azure. This was prompted by an email from Robert Cheetham, CEO at Azavea, who is investigating cloud solutions for their Cicero product. Here are some notes about</p>

<ol>
	<li>setting up and accessing SQL Azure databases</li>
	<li>using SQL Azure with ArcGIS + ArcSquirrel and MapLink</li>
	<li>using SQL Azure with Quantum GIS and MapServer.</li>
</ol>

<h4>Azure Management Portal</h4>

<p>Although I’ve never set up any cloud services before (other than <a href="http://www.arvixe.com/" target="_blank" title="Arvixe web site">Arvixe web hosting</a>) it was incredibly easy to setup a trial SQL Azure subscription (“Windows Azure Platform Introductory Special”), create a database, and add some firewall rules to control access. I didn’t dig deep, but the “Management Portal” seems nice and simple:</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_10.png"><img alt="Azure Management Portal" border="0" height="188" src="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_thumb_4.png" style="padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Azure Management Portal" width="244" />&nbsp;</a></p>

<p>It helpfully tells you your connection string for ADO.Net, ODBC and PHP (passwords not included!):</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_8.png"><img alt="SQL Azure connection strings" border="0" height="180" src="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_thumb_3.png" style="padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="SQL Azure connection strings" width="244" />&nbsp;</a></p>

<p>See this page for <a href="http://www.microsoft.com/windowsazure/offers/popup/popup.aspx?lang=en&amp;locale=en-gb&amp;offer=ms-azr-0018p&amp;no-rewrite=true" target="_blank" title="Details of the Windows Azure Platform Introductory Special">details of the Windows Azure Platform Introductory Special</a> including what you get for no charge and the pricing for the rest.</p>

<h4>MapLink, ArcSquirrel and Azure</h4>

<p>Hartmut connected up our MapLink to the database and pushed some spatial data into my Azure database from ArcGIS – 20,000 polygons took about 75 minutes.&nbsp; Hartmut had the layers drawing in ArcMAP, performing as well as in a normal SQL Server instance apparently, with only minor mods to our code to make the initial connection. However editing will not work without some more tweaks - MapLink and ArcSquirrel rely on some SQL metadata that is a little different on Azure.</p>

<h4>SQL Server Management Studio and MS Access</h4>

<p>Connecting with SSMS is simple, however you get reduced functionality – for example you cannot right-click &gt; edit top 200 records or anything, so quick edits of a small table need to be done with SQL or in MS Access or some other client. Fortunately connecting with MS Access (ODBC + SQL Native Client 10) is also simple, and&nbsp; the Azure database behaves just like a local SQL instance – remarkable querying speed (all things considered), though slower inserts (10,000 records took something like 15 minutes). NB you cannot directly link a spatial table into Access because it doesn’t recognize the geometry data type; but to update attributes you can link a view.</p>

<p>Also, in SSMS you get no designer tools, e.g. for creating a view; everything has to be done with T-SQL.</p>

<h4>SQL Azure Database Manager</h4>

<p>After writing the above I found the database manager in the Azure control panel, which has functionality similar to SSMS, including the ability to edit the tables*:</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_12.png"><img alt="SQL Azure database manager" border="0" height="154" src="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_thumb_5.png" style="padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="SQL Azure database manager" width="244" />&nbsp;</a></p>

<p>* spatial data types not supported for view or edit.</p>

<p>You get a designer for creating/modifying tables, but views and stored procedures are T-SQL only.</p>

<h4>MapServer, OGR and Azure</h4>

<p>Next I wanted to try serving the data through MapServer, and viewing/editing the data in Quantum GIS or MapInfo.&nbsp; MapServer and QGIS require the “geometry_columns” and “spatial_ref_sys” tables, and the easiest way to create these is to push some data in with ogr2ogr.</p>

<p>So I tried this from one of our London servers. I loaded a few hundred linear features, but got four errors like “INSERT command for new feature failed… incorrect syntax…”. On checking the results, a) while the geometries had loaded fine, all the attribute fields were NULL, and b) the spatial_ref_sys table was empty.&nbsp; I had four attribute fields, so my guess is that each syntax error was one field update across all records using a join on the incoming data.</p>

<p>Next I hooked up a MapServer WMS to my SQL Azure tables. MapServer had no trouble connecting to my Azure tables, but initially wouldn’t draw anything…&nbsp; on investigating I found that ogr2ogr had not correctly set the SRID of the geometries, so I had to update them “manually” with:</p>

<pre class="csharpcode">
<span class="kwrd">update</span> esdm_waters <span class="kwrd">set</span> ogr_geometry.STSrid = 4326</pre>
<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<p>(which, incidentally, took 1 minute 52 seconds for 788 rows, demonstrating that updating an Azure database ain’t quick).</p>

<p>Clearly OGR needs a little work to do on SQL Azure support, but this was enough to get me going.</p>

<p>The WMS worked, so to challenge it a little more I set up another WMS onto a table with about 8000 sub-tidal habitat polygons, showing red in this OpenLayers map:</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_2.png"><img alt="MapServer WMS from SQL Azure table" border="0" height="127" src="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_thumb.png" style="padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="MapServer WMS from SQL Azure table" width="244" />&nbsp;</a></p>

<p>And here showing the rivers WMS as well (blue lines):</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_4.png"><img alt="MapServer WMS from SQL Azure table" border="0" height="127" src="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_thumb_1.png" style="padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="MapServer WMS from SQL Azure table" width="244" />&nbsp;</a></p>

<p>Performance of these two layers was perfectly acceptable, though actually about 100% slower than the same WMS working from a local SQL Server 2008 Express instance. It would be interesting to see what it could do with MapServer also running on Azure.</p>

<p>I also tried a WFS – again, performance was acceptable though significantly slower than running with a local SQL instance (though I didn’t do strict comparisons).</p>

<h4>Quantum GIS and SQL Azure</h4>

<p>I found this really helpful blog post to get started: <a href="http://woostuff.wordpress.com/2011/03/13/opening-ms-sql-spatial-in-qgis/" title="http://woostuff.wordpress.com/2011/03/13/opening-ms-sql-spatial-in-qgis/">http://woostuff.wordpress.com/2011/03/13/opening-ms-sql-spatial-in-qgis/</a></p>

<p>Adding the layers requires some python script, but I’m sure someone will write a GUI plugin for QGIS soon.</p>

<p>First make sure you are running a version of QGIS that has GDAL/OGR 1.8 libraries – I’m on 1.7.0 which is OK.</p>

<p>Open the Plugins menu &gt; Python Console</p>

<p>and type in these two commands (modified for your data):</p>

<pre class="csharpcode">
uri = <span class="str">"MSSQL:server={serverName};database={databaseName};tables={tableName};trusted_connection=yes"</span>
qgis.utils.iface.addVectorLayer(uri,<span class="str">'{yourLayerNameHere}'</span>,<span class="str">'ogr'</span>)
</pre>
<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<p>In my case:</p>

<pre class="csharpcode">
uri = <span class="str">"MSSQL:server=tcp:a0awtn7gg9.database.windows.net;database=ArcSquirrel1;tables=esdm_waters;Uid=exegesis@a0awtn7gg9;Pwd=myPassword;"</span>
qgis.utils.iface.addVectorLayer(uri,<span class="rem">'Rivers','ogr')</span>
</pre>

<pre class="csharpcode">
Without any fuss my rivers dataset appeared:</pre>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_14.png"><img alt="SQL Azure table in Quantum GIS" border="0" height="237" src="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_thumb_6.png" style="padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="SQL Azure table in Quantum GIS" width="244" />&nbsp;</a></p>

<p>And I can create and edit features and their attributes, here adding a test line off the Hebrides:</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_16.png"><img alt="Editing SQL Azure features in QGIS" border="0" height="237" src="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_thumb_7.png" style="padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Editing SQL Azure features in QGIS" width="244" />&nbsp;</a></p>

<p>And to prove it, here’s my new feature in OpenLayers…</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_18.png"><img alt="Edited features from QGIS and SQL Azure" src="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_thumb_8.png" style="margin: 0px; width: 244px; height: 127px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Edited features from QGIS and SQL Azure" />&nbsp;</a></p>

<p>And finally, here we have the habitat polygons (EPSG:27700) colour coded by habitat type, as well as the rivers (EPSG:4326), in QGIS. Polygon editing also worked fine, once I’d worked out why my new polygons kept vanishing (doh, classified symbols).</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_20.png"><img alt="SQL Azure polygons thematically mapped in QGIS" border="0" height="237" src="https://www.esdm.co.uk/Data/Sites/1/Windows-Live-Writer/Some-jottings-on-SQL-Azure-and-GIS_5C47/image_thumb_9.png" style="padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="SQL Azure polygons thematically mapped in QGIS" width="244" />&nbsp;</a></p>

<p>Lots to think about, and I look forward to editing the same data with ArcGIS and ArcSquirrel soon…</p>
<br /><a href='https://www.esdm.co.uk/some-jottings-on-sql-azure-and-gis'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/some-jottings-on-sql-azure-and-gis'>...</a>]]></description>
      <link>https://www.esdm.co.uk/some-jottings-on-sql-azure-and-gis</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/some-jottings-on-sql-azure-and-gis</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/some-jottings-on-sql-azure-and-gis</guid>
      <pubDate>Sat, 19 Nov 2011 11:42:00 GMT</pubDate>
    </item>
    <item>
      <title>Notes on importing spatial data into SQL Server 2008</title>
      <description><![CDATA[<p>
	There are a variety of ways to do this, but often you will hit issues with data validity.</p>
<ul>
	<li>
		If you have our ArcGIS Maplink product (or ArcSquirrel) then loading data in using this into a temporary SQL table is probably best as it will do a reasonable amount of checking.</li>
	<li>
		MapInfo has its own SQL loader tool ("Easy Loader" - find it in the Tool Manager)</li>
	<li>
		If you are just using open source you can use OGR2OGR</li>
</ul>
<p>
	ogr2ogr -overwrite -f "MSSQLSpatial" "MSSQL:server=MyServer;database=MyDatabase;Trusted_Connection=yes;" "C:\Temp\MyShapeFile.shp" -a_srs "EPSG:27700"</p>
<p>
	Once the data is in SQL there can still be issues:</p>
<h2>
	Incorrect SRID</h2>
<ul>
	<li>
		Make sure you specified the correct SRID when the data was loaded (if you didn’t specify it – it may well be 0). You can check this with the SQL: SELECT geom.STSrid from SitesGeom Where geom.STSrid &lt;&gt; 27700</li>
</ul>
<h2>
	Invalid ring</h2>
<ul>
	<li>
		You may also have invalid ring geometries – a good post on this is <a href="http://blogs.msdn.com/b/edkatibah/archive/2008/08/19/working-with-invalid-data-and-the-sql-server-2008-geography-data-type-part-1b.aspx"><font color="#0066cc">http://blogs.msdn.com/b/edkatibah/archive/2008/08/19/working-with-invalid-data-and-the-sql-server-2008-geography-data-type-part-1b.aspx</font></a> but the key SQL command is:</li>
</ul>
<p>
	UPDATE foo SET geom = geom.STUnion(geom.STStartPoint());</p>
<h2>
	Invalid geometries</h2>
<p>
	UPDATE foo SET geom = geom.MakeValid()</p>
<h2>
	SRID not imported</h2>
<p>
	Import geometry data into temp SQL table (e.g. foo) – with tempGeom column</p>
<p>
	Then try to populate a geometry column from the WKB of the validated column:</p>
<p>
	ALTER TABLE foo ADD geomFinal geometry;</p>
<p>
	SET geomFinal = geometry::STGeomFromWKB(geomTemp.STAsBinary(), 27700)</p>
<br /><a href='https://www.esdm.co.uk/notes-on-importing-spatial-data-into-sql-server-2008'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/notes-on-importing-spatial-data-into-sql-server-2008'>...</a>]]></description>
      <link>https://www.esdm.co.uk/notes-on-importing-spatial-data-into-sql-server-2008</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/notes-on-importing-spatial-data-into-sql-server-2008</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/notes-on-importing-spatial-data-into-sql-server-2008</guid>
      <pubDate>Thu, 17 Nov 2011 09:24:00 GMT</pubDate>
    </item>
    <item>
      <title>Where to find the Ordnance Survey registration files</title>
      <description><![CDATA[<p>The registration files (world files and tab files) for all Ordnance Survey raster datasets can be downloaded from: <p><a title="http://www.ordnancesurvey.co.uk/oswebsite/products/geo-referencing.html" href="http://www.ordnancesurvey.co.uk/oswebsite/products/geo-referencing.html" target="_blank">http://www.ordnancesurvey.co.uk/oswebsite/products/geo-referencing.html</a> <p>These are available as *.tfw "world files" for ArcGIS<sup>®</sup>, or *.tab files for MapInfo Professional<sup>®</sup></p><br /><a href='https://www.esdm.co.uk/where-to-find-the-ordnance-survey-registration-files'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/where-to-find-the-ordnance-survey-registration-files'>...</a>]]></description>
      <link>https://www.esdm.co.uk/where-to-find-the-ordnance-survey-registration-files</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/where-to-find-the-ordnance-survey-registration-files</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/where-to-find-the-ordnance-survey-registration-files</guid>
      <pubDate>Tue, 15 Nov 2011 14:21:04 GMT</pubDate>
    </item>
    <item>
      <title>Using ECWs without world files in MapInfo</title>
      <description><![CDATA[<p>If you have ECW map images without world files, they may work fine in ArcGIS but MapInfo won't put them in the right place as it can't read the header information in the ECW. You could opening them individually in MapImagery to generate tabs, but this is very time consuming.</p>

<p>We have written a custom MapBasic app for this purpose: for ESDM staff this is located in 3rdPartyApps\TABs_from_ECW [this being a pretty dumb place for it, as it is not a 3rdPartyApp, Ed]. Instructions are in the text file in the zip.</p>

<p>If you are not Exegesis staff, and find yourself in this situation, please <a href="https://www.esdm.co.uk/contact">contact us</a> for help.</p>
<br /><a href='https://www.esdm.co.uk/using-ecws-without-world-files-in-mapinfo-1'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/using-ecws-without-world-files-in-mapinfo-1'>...</a>]]></description>
      <link>https://www.esdm.co.uk/using-ecws-without-world-files-in-mapinfo-1</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/using-ecws-without-world-files-in-mapinfo-1</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/using-ecws-without-world-files-in-mapinfo-1</guid>
      <pubDate>Sat, 12 Nov 2011 18:51:00 GMT</pubDate>
    </item>
    <item>
      <title>GIS conversion web site</title>
      <description><![CDATA[<p>This site can convert just about anything to just anything else. E.g. I used it to do MapInfo to KML and GPX.</p>
<p>I think it uses ogr2ogr under the bonnet.</p>
<p><a href="http://mygeodata.eu/apps/converter/index_EN.html" target="_new">http://mygeodata.eu/apps/converter/index_EN.html</a></p><br /><a href='https://www.esdm.co.uk/gis-conversion-web-site'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/gis-conversion-web-site'>...</a>]]></description>
      <link>https://www.esdm.co.uk/gis-conversion-web-site</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/gis-conversion-web-site</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/gis-conversion-web-site</guid>
      <pubDate>Tue, 30 Nov 2010 20:32:00 GMT</pubDate>
    </item>
  </channel>
</rss>