 <?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~-1" rel="self" type="application/rss+xml" />
    <itunes:owner />
    <itunes:explicit>no</itunes:explicit>
    <item>
      <title>When GeoServer SQL Server stores won’t load on a new server</title>
      <description><![CDATA[<p>We’ve been migrating a large GeoServer instance onto a new Windows 2019 server. The old GeoServer instance was running version 2.7, the new one version 2.15.1. Nearly everything “just worked” after copying the old data directory into the new location, however several SQL Server stores didn’t appear.</p>

<p>This kind of thing was seen for each store in the GeoServer logs:</p>

<p><code>2020-06-01 13:26:43,677 WARN [org.geoserver] - Failed to load data store 'MyStoreName'<br />
com.thoughtworks.xstream.converters.ConversionException:<br />
---- Debugging information ----<br />
cause-exception&nbsp;&nbsp;&nbsp;&nbsp; : org.jasypt.exceptions.EncryptionOperationNotPossibleException<br />
cause-message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : null<br />
class&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : org.geoserver.catalog.impl.DataStoreInfoImpl<br />
required-type&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : org.geoserver.catalog.impl.DataStoreInfoImpl<br />
converter-type&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : org.geoserver.config.util.XStreamPersister$StoreInfoConverter<br />
line number&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 31<br />
version&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 2.15.1<br />
-------------------------------</code></p>

<p>First we verified that we could create a new SQL Server store using the GeoServer administrative interfaces – this proved that the plugin for SQL Server and the various driver files were installed and working correctly.</p>

<p>So why were the old stores not working? The store database connection for each store is defined in a file like this:</p>

<p>"D:\Geoserver_data_dir\workspaces\MyStoreName\MyStoreName\datastore.xml"</p>

<p>and in this file we find a line like this:</p>

<p><code>&lt;entry key="passwd"&gt;crypt1:ywSBY9D+HjlxxL/SuRwBPVmT8P98f47M&lt;/entry&gt;</code></p>

<p>The solution is to delete that line in the definition file for each store, then restart GeoServer. The stores then appear in the GeoServer admin pages, and you can edit each one to re-enter the password, after which any layers should be operational.</p>
<br /><a href='https://www.esdm.co.uk/when-geoserver-sql-server-stores-won’t-load-on-a-new-server'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/when-geoserver-sql-server-stores-won’t-load-on-a-new-server'>...</a>]]></description>
      <link>https://www.esdm.co.uk/when-geoserver-sql-server-stores-won’t-load-on-a-new-server</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/when-geoserver-sql-server-stores-won’t-load-on-a-new-server</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/when-geoserver-sql-server-stores-won’t-load-on-a-new-server</guid>
      <pubDate>Mon, 01 Jun 2020 13:25:00 GMT</pubDate>
    </item>
    <item>
      <title>How to remove .png files recursively from a folder tree without removing .png8 files too</title>
      <description><![CDATA[<p>We have some large GeoWebCache caches of Ordnance Survey map tiles in .png8 format. Unfortunately a few million .png files have crept into the caches too (accidental bad config at some point), duplicating the .png8 tiles and using up a lot more space. I wanted to delete these.</p>

<p>At first I thought "simple:-&nbsp;do a search in Windows Explorer for .png and delete". Ah no, that selects all the .png8 files as well, plus my number of folders and files were far too large.</p>

<p>Then I thought... <code>del /S *.png</code></p>

<p>But no, this deletes the .png8 files as well, grrr.</p>

<p>So I wrote a simple python script, that I will lodge here for future reference. It's Python 2.7, but I imagine would work in 3.* with little or no change. The final print statement probably slows things down, but I wanted to be able to see what was going on.</p>

<pre>
import os
indir = 'M:\\MyVeryLargeCachePath'
for root, dirs, filenames in os.walk(indir):
    for f in filenames:
        if os.path.splitext(f)[1] == '.png':
            os.remove(os.path.join(root, f))
            print('deleted ' + os.path.join(root, f))</pre>

<p>Assuming you have Python installed, simply save this as a file with a .py extension, modify the path and the file extensions to suit, then run it.</p>

<p>This same technique would work for other combinations of file extensions, e.g. to remove *.doc but not *.docx, or to remove *.xls while retaining *.xslx</p>
<br /><a href='https://www.esdm.co.uk/how-to-remove-png-files-recursively-from-a-folder-tree-without-removing-png8-files-too'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/how-to-remove-png-files-recursively-from-a-folder-tree-without-removing-png8-files-too'>...</a>]]></description>
      <link>https://www.esdm.co.uk/how-to-remove-png-files-recursively-from-a-folder-tree-without-removing-png8-files-too</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/how-to-remove-png-files-recursively-from-a-folder-tree-without-removing-png8-files-too</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/how-to-remove-png-files-recursively-from-a-folder-tree-without-removing-png8-files-too</guid>
      <pubDate>Wed, 27 May 2020 09:09:00 GMT</pubDate>
    </item>
    <item>
      <title>When MapServer refuses to run in an ASP.NET Core MVC site (401 - unauthorized)</title>
      <description><![CDATA[<p>Well that was two hours I won’t get back, but I’ll make a note here in case it helps anyone else. I had simply overlooked one of those many tiny things…</p><p>We use MapServer as a lightweight GIS engine in many of our products, giving us WMS/WFS API endpoints for a wide range of purposes. When upgrading the <a href="https://colchesterheritage.co.uk" target="_blank">Colchester Heritage Explorer website</a>, an ASP.NET Core MVC site made with <a href="https://www.cloudscribe.com" target="_blank">cloudscribe</a> and <a href="https://hbsmrdocumentation.esdm.co.uk/hbsmr-webapi" target="_blank">HBSMR WEB/API</a> components, I dived in to configure and test the MapServer instance. I followed our usual processes, enabled the CGI extension to execute, setting folder permissions to allow MapServer to write out log files, configured our secure MapServer Proxy component, etc. </p><p>But it wasn’t working. </p><p>One of our first tests of a MapServer instance is to browse to the location of the main MapServer executable, in this case the address was:</p><p>https://colchesterheritage.co.uk/wwwroot/MapServer/Scripts/mapserv.exe </p><p>but instead of a nice diagnostic response, I got “401 – Unauthorized: Access is denied due to invalid credentials”:</p><p><img width="640" height="116" title="image" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_160.png" border="0"></p><p>This was very puzzling, as I already had this working perfectly in a parallel test site, on the same server with identical configuration in every respect – or so I thought. Both sites were using an application pool with Identity set to ApplicationPoolIdentity, folder permissions were all good for this identity, there were no ip restrictions, and no reason I could see for it not to work.</p><p>Except that I’d forgotten about the Anonymous Authentication Credentials… in IIS select the site &gt; Authentication &gt; select Anonymous Authentication and click “Edit…” &gt; I found the default setting:</p><p><img width="640" height="328" title="image" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_161.png" border="0"></p><p>And of course I had not set up permissions for this user to access the location of the MapServer files. So there are two solutions: a) <em>less preferred</em>: add the IUSR account at the root of site with default permissions:</p><p><img width="378" height="457" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_162.png" border="0"></p><p>(if I were using this solution I would also tick “Deny” for the “Write” setting off bottom of that image);</p><p>or b) <em>preferred</em>: change the Anonymous Authentication Credentials for the site to the Application pool identity:</p><p><img width="640" height="330" title="image" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_163.png" border="0"></p><p>After making this change, on browsing to our MapServer executable I saw the expected response “Noquery information to decode. QUERY_STRING is set, but empty. ” which means the CGI application is working. </p><p><strong>Phew – problem solved!</strong></p><p>My next step in hardening this site is to block this direct access, and route all requests through a secured proxy, so the above URL to the exe won’t work by the time you read this if we’ve done our security right.</p><p>To see the working result, check out the maps within the <a href="https://colchesterheritage.co.uk" target="_blank">Colchester Heritage Explorer</a>, for example this map of the historic buildings and sites that are <a href="https://colchesterheritage.co.uk/map?center=100120.11461153519,6780076.084676751&amp;zoom=16&amp;baselayer=OpenStreetMap&amp;opacity=1&amp;panel=overlays&amp;overlays=LocalList&amp;overlaysoff=RegisteredParksAndGardens,ConservationAreas,ListedBuildings,ScheduledMonuments,Events,Monuments" target="_blank">included on the Colchester “Local List”</a>.</p><p>And here’s a rather nice map of WW2 pillboxes and related defensive sites around Colchester, generated by a MapServer WFS request in response to a <a href="https://colchesterheritage.co.uk/globalsearch/index?q=pillboxes" target="_blank">a free text search on “pillboxes”</a>:</p><p><img width="559" height="480" title="image" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_164.png" border="0"></p><p>I hope this helps someone, and please feel free to add comments below.</p><br /><a href='https://www.esdm.co.uk/when-mapserver-refuses-to-run-in-an-aspnet-core-mvc-site-401-unauthorized'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/when-mapserver-refuses-to-run-in-an-aspnet-core-mvc-site-401-unauthorized'>...</a>]]></description>
      <link>https://www.esdm.co.uk/when-mapserver-refuses-to-run-in-an-aspnet-core-mvc-site-401-unauthorized</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/when-mapserver-refuses-to-run-in-an-aspnet-core-mvc-site-401-unauthorized</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/when-mapserver-refuses-to-run-in-an-aspnet-core-mvc-site-401-unauthorized</guid>
      <pubDate>Sat, 19 Oct 2019 10:55:24 GMT</pubDate>
    </item>
    <item>
      <title>A Guide to the Ordnance Survey Archaeological Records</title>
      <description><![CDATA[<p>Our <a href="https://www.esdm.co.uk/hbsmr-historic-environment">HBSMR </a>application is for collecting and managing Historic Environment Record (HER) data, usually as the permanent inventory for an area, and sometimes for short-term data collection or research initiatives.</p>

<p>Historic Environment Records evolved from the County "Sites and Monuments Records" that developed in the 1970s and 1980s, often pioneering the use of the emerging database technologies of the day. These "SMRs" in turn owe their origins to the records collected by the Ordnance Survey ("OS"), driven by <a href="https://en.wikipedia.org/wiki/O._G._S._Crawford" target="_blank">O.G.S. Crawford in the 1920s and 1930s</a>, which used a systematic method of cataloguing sites on index cards and 1:10,000 (or earlier 1 mile=6") map sheets.&nbsp;</p>

<p>This fascinating guide to the Ordnance Survey records was recently found in the records of the Somerset Archaeological Society. It was presumably distributed by Ordnance Survey whenever they sent out copies of the cards.</p>

<h2>The guide</h2>

<blockquote>
<h4>THE ORDNANCE SURVEY'S ARCHAEOLOGICAL RECORDS</h4>

<p>A. INTRODUCTION</p>

<p>The records consist of 8" x 5" index cards ('Record Cards') which give information about antiquities, and 1:10,000/1:10,560 maps ('Record Sheets') on which the sites of the antiquities are marked. The Record Card is described under B, the Record Sheet under C, and under D is a Glossary of the common abbreviations used on both cards and sheets.</p>

<p>B. THE RECORD CARD</p>

<p>The component parts of the current record card are as in Fig 1 below. There were some differences on older cards though the same purposes were served. The main differences are noted in the explanation.</p>

<p><img alt="" height="295" src="https://www.esdm.co.uk/Data/Sites/1/media/oscard_fig1.jpg" width="761" /></p>

<figure>
<figcaption><em>Fig 1</em></figcaption>
</figure>

<p>&nbsp;</p>

<p>1. ANTIQUITY NUMBER</p>

<p>This consists of the National grid number of the 1:10,000/10,560 sheet within which the antiquity falls (eg TQ 37 SW) plus a serial number within that sheet (eg TQ 37 SW 4), and used in its full form identifies the antiquity uniquely. The serial numbers are simply allotted consecutively as each record is compiled, and consequently have no bearing on where the site falls within the sheet, nor any typological significance.</p>

<p>There can also be subsidiary number (eg TQ 37 SW 4.1), used when a component part of an antiquity is allotted a card to itself.</p>

<p>There can in addition be a 'Miscellaneous Information card' for any sheet. This gives very brief information on items of marginal interest which fall on the sheet, and there is only one such card (or card series) for each sheet. It is identified by the sheet number plus a bracketed 'M' (e.g. TQ 37 SW (M)).</p>

<p>2. COUNTY</p>

<p>The name of the county at the date of the last entry. Abbreviations are those adopted by the Ordnance Survey.</p>

<p>3. PARISH</p>

<p>The name of the civil parish at the date of the last entry.</p>

<p>4. CLASSIFICATION</p>

<p>Abbreviations of the archaeological period(s) within which the subject(s) of a card falls, and also abbreviations to show that the period is unestablished, or that the subject of the record not an antiquity (within OS specification). The abbreviations and their meanings are given in the glossary under D below.</p>

<p>5. SPARE</p>

<p>Reserved for future extension of the classification.</p>

<p>6. PAGE NUMBER</p>

<p>The card side number whenever the record extends over than one card side.</p>

<p>7. SCHEDULING</p>

<p>The letter 'S' is entered when the antiquity is scheduled as an Ancient Monument by the Department Of the Environment. On old cards, scheduling was mentioned as text information and authorized. 'S's have not been comprehensively entered, and the lack of an 'S' is no guarantee that the antiquity is unscheduled.</p>

<p>8. SITING SUMARY</p>

<p>A Map-reference of the latest established site or find-spot of the antiquity, whenever this is known to at least a six-figure accuracy (i.e. to within about 100 metres). When siting is less precisely known, the name of the town, village or other place associated with it is entered with a four-figure reference. On old cards the siting summary was in the space serving for both purposes.</p>

<p>9. TYPOLOGY SUMMARY</p>

<p>A brief summary of the (latest established) nature of the antiquity in modern terms, taken from the information recorded, and accounting for each item in the record which has a different period classification.</p>

<p>10. PUBLICATION DIRECTION</p>

<p>Direction (for internal use) as to whether the antiquity is to be published on standard basic scale map, and if so the form the name should take and the type character to be used.</p>

<p>11. TEXT</p>

<p>The information necessary to demonstrate the siting, nature and condition of the antiquity (or to give as much as possible towards those ends), taken from whatever sources provide it, these sources being shown in the adjoining 'Authorities' part - see 12 below. The information can be entered either 'objectively' (i.e. by separate precis from each source) or 'subjectively' (i.e. by a single entry compiled from as many sources as are necessary), or by a mixture of these methods.</p>

<p>Normally the record will start with the objective entry of an Ordnance Survey publication. In subjective entries references are given to the various sources by use of the author's name or bracketed numbers. In objective entries the whole of the text is attributed to a single authority. The recorder can himself make interpolated comments, or even a separate entry of his own opinions. Field reports are always attributable to the Field Investigator himself unless otherwise stated.</p>

<p>12. AUTHORITIES</p>

<p>Each authority (source) consulted and used is given a number. With objective recording this number, with the title of the authority concerned, is positioned opposite the top line of the relevant text entry. With subjective recording, the numbers and authority titles are listed in sequence without interval, the first being opposite the first line of the text entry.</p>

<p>Secondary authorities (or sub-references) are lettered in lower case, and this lettering is in sequence throughout any one record (i.e. letters are not repeated even though under a different primary authority). Secondary authorities are only noted when they make necessary contributions but do not need to be used as primary authorities; it should therefore be appreciated that the primary authorities contain references which are not noted on the record cards. Secondary authorities may in fact have been consulted by the recorder, but the implication of the identification by lower case letters is that they have not. The recorder responsible for entries is identified by R1, R2 etc, with his initials and date, entered opposite the line of the last entry. The Field Investigator is similarly identified by F1, F2 etc, entered opposite the top line of his field report (and covering any recording between that entry and the last recorder's ascription). In the field reports informants are usually treated as secondary authorities, but they can be given separate entries as primary authorities. On old cards Recorders were designated 'OR' and Field Investigators FI and full names were given.</p>

<p>References dealing with relevant typology, but not with the specific antiquity which is the subject of a record, are mentioned in the text, not the Authorities compartment.</p>

<p>13. ILLUSTRATIONS</p>

<p>Illustrations are mounted on separate 'Illustration Cards' numbered in the same way as the record cards. The illustrations may be air photographs, ground photographs, diagrams, plans, or surveys, and they are folded to size if necessary. The photographs do not reproduce.</p>

<p>All Archaeology Division field surveys are ultimately included as illustrations, but there is delay in annexing them, as they have to pass through drawing stage (for the production of new OS maps) before becoming available.</p>

<p>C. THE RECORD SHEET</p>

<p>An example of the current record sheet marking is given in Fig 2 below. The essential ingredients are:</p>

<p>a. a convention marking the site</p>

<p>b. the antiquity number</p>

<p>c. a typological summary.</p>

<p>There were some differences on older record sheets though the same purposes were served. The main differences are noted in the explanations.</p>

<figure><img alt="Ordnance Survey Archaeology Record Sheet Fig2" src="https://www.esdm.co.uk/Data/Sites/1/media/knowledgebase/blog/os_archaeology_fig2.jpg" />
<figcaption><em>Fig 2 (Examples fictitious)</em></figcaption>
</figure>

<p>&nbsp;</p>

<p>1. SITE MARKING</p>

<p>Various markings are used, and on the master copy record sheets different colours are also employed for further refinement. But the main purposes of the record sheets are to serve as indices to the record cards and to record the sites of antiquities topographically, and these purposes are just as well served by monochrome copies. All that is lost are certain nuances of siting and typology useful for quick reference but fully covered on the record cards.</p>

<p>The markings are: Crosses, Published detail, Sketched outline, Bands, Linear markings and Broken linear markings.</p>

<p>The following explanations deal with their monochrome fom only, so more than one meaning sometimes attaches to a single convention. The separate implications however can be readily clarified by reference to the record cards.</p>

<p>Crosses</p>

<p>a. Accurate find-spot or site, whether or not extant (Ex 5).</p>

<p>b. Inaccurate site or find spot (Ex 1).</p>

<p>c. Centre of an extensive antiquity when the outline cannot be properly recovered (Ex 11).&nbsp;</p>

<p>Published detail</p>

<p>This is normally left to stand by itself (Exs 9, 2.1) but it can be shaded or blocked in when it is necessary to distinguish it from adjacent features (Ex 12).&nbsp;</p>

<p>Sketched outline</p>

<p>This is used in preference to a cross for large antiquities which are not published on the record sheet (Ex 10).</p>

<p>Bands</p>

<p>a. Defining a group of antiquities treated on a single record card (Ex 4).</p>

<p>b. Defining the known outline of a feature or site with no perimeter feature (eg a DMV) (Ex 2).</p>

<p>c. Defining the area within which an antiquity occurs, when that area is already defined by published detail, such as hedges (Ex 7).</p>

<p>Linear Markings</p>

<p>The course of a linear antiquity; including extensive enclosure banks etc such as park pales (Exs RR46, Lin 21).</p>

<p>Broken linear markings</p>

<p>The course of a linear antiquity when uncertain (Ex RR46).</p>

<p>2. ANTIQUITY NUMBERS</p>

<p>A plain number in a circle is the number allotted to the record card. It should be noted that to identify the antiquity uniquely the sheet number must be added to this (Exs 1-12).</p>

<p>A decimalised number in a circle refers to a subsidiary record (cf B 1 above) (Ex 2.1).</p>

<p>A smaller number in brackets applies to miscellaneous information (cf B 1 above) (Ex (1)).</p>

<p>The entry of a number in the east margin of the sheet conveys that the antiquity concerned probably falls on that sheet but cannot be sited with any accuracy (Exs 6, 8).</p>

<p>A full number in brackets entered in the margin adjacent to the marking of an antiquity which extends over the edge shows that the card is allotted to the adjacent sheet (Ex (TQ 37 NW6), in north margin).</p>

<p>Linear antiquities are numbered according to a separate Linear system, which is not controlled by sheets. Roman Roads are given the Margary numbers ('Roman Roads in Britain'), otherwise the numbering system is the Ordnance Survey's. Linear numbering is entered on the sheets in brackets, normally along the axis of the Linear (Exs RR46, Lin 21).</p>

<p>3. TYPOLOGICAL SUMMARIES</p>

<p>These are similar to the typological summaries on the record cards (cf B9 above), though they need not be exactly the same.&nbsp; They are entered on the sheets for convenience and as a safeguarding connection with the record card, additional to the number.</p>

<p>D. GLOSSARY OF ABBREVIATIONS&nbsp;USED ON THE ORDNANCE SURVEY'S ARCHAEOLOGICAL RECORD CARDS AND SHEETS</p>

<p>This is not comprehensive, but should account for the majority of the abbreviations which might be puzzling. It does not account for the book title abbreviations used in the 'Authorities' part of the record card, which should be easily understood, nor for the initials of the compiling staff (see B 12 above) Which are irrelevant to the understanding of the records.</p>

<table border="1" cellpadding="2" cellspacing="0">
	<thead>
		<tr>
			<th class="text-center" scope="col">ABBREVIATION</th>
			<th class="text-center" scope="col">MEANING</th>
			<th class="text-center" scope="col">AREA OF OCCURRENCE<br />
			<span class="font-small">(Number of component<br />
			part of record card,<br />
			as in Fig 1, or 'S')</span></th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>AAO</td>
			<td><span style="display: inline !important; float: none; background-color: transparent; color: rgb(51, 51, 51); font-family: &quot;Open Sans&quot;,sans-serif; font-size: 19px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">Assistant Archaeology Officer</span></td>
			<td>12</td>
		</tr>
		<tr>
			<td>Acc No</td>
			<td>Museum Accession number</td>
			<td>11</td>
		</tr>
		<tr>
			<td>Accn No</td>
			<td>" &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; " &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; "&nbsp;</td>
			<td>11</td>
		</tr>
		<tr>
			<td>AO</td>
			<td>Archaeology Officer</td>
			<td>12</td>
		</tr>
		<tr>
			<td>"</td>
			<td>Archaeology Division</td>
			<td>12</td>
		</tr>
		<tr>
			<td>AP</td>
			<td>Air photograph</td>
			<td>11, 12</td>
		</tr>
		<tr>
			<td>AS</td>
			<td>Anglo-Saxon</td>
			<td>9, 11, S</td>
		</tr>
		<tr>
			<td>AT</td>
			<td>Antiquity Tpe character</td>
			<td>10, 11</td>
		</tr>
		<tr>
			<td>Authy</td>
			<td>Authority</td>
			<td>11</td>
		</tr>
		<tr>
			<td>BA</td>
			<td>Bronze Age</td>
			<td>4, 9, 11, S</td>
		</tr>
		<tr>
			<td>BM</td>
			<td>British Museum</td>
			<td>11</td>
		</tr>
		<tr>
			<td>C or c</td>
			<td>Century</td>
			<td>9, 11</td>
		</tr>
		<tr>
			<td>c</td>
			<td>Circa</td>
			<td>11</td>
		</tr>
		<tr>
			<td>Corr</td>
			<td>Ordnance Survey arch. correspondent&nbsp;</td>
			<td>12</td>
		</tr>
		<tr>
			<td>Corr 6"</td>
			<td>Correspondent's annotated 6" sheet</td>
			<td>12</td>
		</tr>
		<tr>
			<td>Crem</td>
			<td>Cremation</td>
			<td>9, 11</td>
		</tr>
		<tr>
			<td>DA</td>
			<td>Dark Ages</td>
			<td>9, 11, S</td>
		</tr>
		<tr>
			<td>DOE</td>
			<td>Department of the Environment</td>
			<td>12</td>
		</tr>
		<tr>
			<td>DMV</td>
			<td>Deserted medieval village</td>
			<td>9, 11, S</td>
		</tr>
		<tr>
			<td>diss</td>
			<td>Dissolved (monasteries)</td>
			<td>11</td>
		</tr>
		<tr>
			<td>EBA</td>
			<td>Early Bronze Age</td>
			<td>9, 11, S</td>
		</tr>
		<tr>
			<td>EI</td>
			<td>Early Industrial</td>
			<td>4, S</td>
		</tr>
		<tr>
			<td>EIA</td>
			<td>Early Iron Age</td>
			<td>9, 11, S</td>
		</tr>
		<tr>
			<td>Ewk</td>
			<td>Earthwork</td>
			<td>9, 11, S</td>
		</tr>
		<tr>
			<td>Ed</td>
			<td>Editor</td>
			<td>11, 12</td>
		</tr>
		<tr>
			<td>Ed</td>
			<td>Edition</td>
			<td>11, 12</td>
		</tr>
		<tr>
			<td>F1, F2 etc</td>
			<td>First, second, etc OS Arch fd investigator&nbsp;</td>
			<td>12</td>
		</tr>
		<tr>
			<td>fd</td>
			<td>Found</td>
			<td>11, S</td>
		</tr>
		<tr>
			<td>"</td>
			<td>Founded</td>
			<td>11, S</td>
		</tr>
		<tr>
			<td>GP</td>
			<td>Ground photograph</td>
			<td>11, 12</td>
		</tr>
		<tr>
			<td>GS</td>
			<td>Gil Sans type character</td>
			<td>10, 11</td>
		</tr>
		<tr>
			<td>GT</td>
			<td>German Text type character</td>
			<td>10, 11</td>
		</tr>
		<tr>
			<td>HHR</td>
			<td>DOE Historic Houses record</td>
			<td>12</td>
		</tr>
		<tr>
			<td>IA</td>
			<td>Iron Age</td>
			<td>4, 9, 11, S</td>
		</tr>
		<tr>
			<td>IAM</td>
			<td>DOE Inspectorate of Ancient Monuments</td>
			<td>11, 12</td>
		</tr>
		<tr>
			<td>Inf</td>
			<td>Informant, or Information from</td>
			<td>12</td>
		</tr>
		<tr>
			<td>Inhum</td>
			<td>Inhumation</td>
			<td>9, 11</td>
		</tr>
		<tr>
			<td>LB</td>
			<td>Lutheran Black type character (Obs)</td>
			<td>11</td>
		</tr>
		<tr>
			<td>LB</td>
			<td>Long Barrow</td>
			<td>11</td>
		</tr>
		<tr>
			<td>LBA</td>
			<td>Late Bronze Age</td>
			<td>9, 11, S</td>
		</tr>
		<tr>
			<td>Lin</td>
			<td>Class name for linear feature other than a Roman road&nbsp;</td>
			<td>11, S</td>
		</tr>
		<tr>
			<td>(M)</td>
			<td>Miscellaneous Information card</td>
			<td>1</td>
		</tr>
		<tr>
			<td>MBA</td>
			<td>Middle Bronze Age</td>
			<td>9, 11, S</td>
		</tr>
		<tr>
			<td>Md</td>
			<td>Medieval</td>
			<td>9, 11, S</td>
		</tr>
		<tr>
			<td>Me</td>
			<td>Mesolithic</td>
			<td>4, 9, 11, S</td>
		</tr>
		<tr>
			<td>MHLG</td>
			<td>Ministry of Housing &amp; Local Govt (Obs)</td>
			<td>12</td>
		</tr>
		<tr>
			<td>MOW</td>
			<td>Ministry of Works (Obs)</td>
			<td>12</td>
		</tr>
		<tr>
			<td>MPBW</td>
			<td>Ministry of Pub Works &amp; Buildings (Obs)</td>
			<td>12</td>
		</tr>
		<tr>
			<td>Mus 6"</td>
			<td>Annotated Museum 6" sheet</td>
			<td>12</td>
		</tr>
		<tr>
			<td>NAT</td>
			<td>Non-antiquity type character</td>
			<td>10, 11</td>
		</tr>
		<tr>
			<td>Ne</td>
			<td>Neolithic</td>
			<td>4, 9, 11, S</td>
		</tr>
		<tr>
			<td>NMR</td>
			<td>National Monument Record</td>
			<td>11, 12</td>
		</tr>
		<tr>
			<td>NR</td>
			<td>Non-Roman Type character</td>
			<td>10, 11</td>
		</tr>
		<tr>
			<td>Occ</td>
			<td>Occupation</td>
			<td>9, 11, S</td>
		</tr>
		<tr>
			<td>ONB</td>
			<td>Ordnance Survey 'Object Name Book'</td>
			<td>11, 12</td>
		</tr>
		<tr>
			<td>OR</td>
			<td>Office Recorder (OS staff) (Obs)</td>
			<td>12</td>
		</tr>
		<tr>
			<td>OS</td>
			<td>Ordnance Survey</td>
			<td>11, 12</td>
		</tr>
		<tr>
			<td>P</td>
			<td>Continue to publish in same form</td>
			<td>10</td>
		</tr>
		<tr>
			<td>Pa</td>
			<td>Palaeolithic</td>
			<td>4, 9, 11, S</td>
		</tr>
		<tr>
			<td>Poss</td>
			<td>Possible</td>
			<td>9, 11, S</td>
		</tr>
		<tr>
			<td>PN</td>
			<td>Post-Norman</td>
			<td>4</td>
		</tr>
		<tr>
			<td>PR</td>
			<td>Post-Roman</td>
			<td>4</td>
		</tr>
		<tr>
			<td>Prob</td>
			<td>Probably</td>
			<td>9, 11, S</td>
		</tr>
		<tr>
			<td>Pte 6"</td>
			<td>Private 6" sheet, annotated</td>
			<td>12</td>
		</tr>
		<tr>
			<td>R1, R2 etc</td>
			<td>First, second, etc OS Arch recorder</td>
			<td>12</td>
		</tr>
		<tr>
			<td>Rec 6"</td>
			<td>Records 6" (obsolete annotated sheet)</td>
			<td>12</td>
		</tr>
		<tr>
			<td>Rems</td>
			<td>Remains of</td>
			<td>9, 11, S</td>
		</tr>
		<tr>
			<td>RB</td>
			<td>Romano-British</td>
			<td>9, 11, S</td>
		</tr>
		<tr>
			<td>R &amp; F</td>
			<td>Ridge and furrow</td>
			<td>9, 11</td>
		</tr>
		<tr>
			<td>Ro</td>
			<td>Roman</td>
			<td>4, 9, 11, S</td>
		</tr>
		<tr>
			<td>RR</td>
			<td>Roman Road with Margary number</td>
			<td>1, 9, 11, S</td>
		</tr>
		<tr>
			<td>RRX</td>
			<td>Roman Road without Margary number</td>
			<td>1, 9, 11, S</td>
		</tr>
		<tr>
			<td>S</td>
			<td>Scheduled by IAM (qv)</td>
			<td>7</td>
		</tr>
		<tr>
			<td>SS Rev</td>
			<td>OS Small Scales reviser</td>
			<td>12</td>
		</tr>
		<tr>
			<td>S/Svyr</td>
			<td>Senior Surveyor (Archaeology Division)</td>
			<td>12</td>
		</tr>
		<tr>
			<td>TI</td>
			<td>Times Italic type character</td>
			<td>11</td>
		</tr>
		<tr>
			<td>TU</td>
			<td>Times Upright type character</td>
			<td>11</td>
		</tr>
		<tr>
			<td>U</td>
			<td>Period unestablished</td>
			<td>4, 9</td>
		</tr>
		<tr>
			<td>X</td>
			<td>Not an antiquity</td>
			<td>4</td>
		</tr>
		<tr>
			<td>X</td>
			<td>Do not publish on standard maps</td>
			<td>10</td>
		</tr>
		<tr>
			<td>&nbsp;</td>
			<td>&nbsp;</td>
			<td>&nbsp;</td>
		</tr>
	</tbody>
</table>

<p>&nbsp;</p>
</blockquote>
<br /><a href='https://www.esdm.co.uk/a-guide-to-the-ordnance-survey-archaeological-records'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/a-guide-to-the-ordnance-survey-archaeological-records'>...</a>]]></description>
      <link>https://www.esdm.co.uk/a-guide-to-the-ordnance-survey-archaeological-records</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/a-guide-to-the-ordnance-survey-archaeological-records</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/a-guide-to-the-ordnance-survey-archaeological-records</guid>
      <pubDate>Sun, 07 Apr 2019 17:58:00 GMT</pubDate>
    </item>
    <item>
      <title>Configuring Geoserver with Geo web cache (GWC)</title>
      <description><![CDATA[<p>The aim of this is to improve the performance of our mapping (obvious really I hope) both in terms of the speeds a user experiences and in terms of the number of users our server can support.</p>

<p>In this post we are assuming we are looking at WMS – so image caching.</p>

<p>There are a couple of pre-requisites to get caching to work:</p>

<ul>
	<li>Your WMS requests must be using a recognised tiling system in the same coordinate system as you build your cache.</li>
	<li>You must be requesting your WMS as tiles</li>
</ul>

<h1>Consider your data</h1>

<p>Before launching into building tile caches its a good idea to stop and think about your data.&nbsp;</p>

<h2>Is the resolution of my data appropriate</h2>

<p>If its vector data for example can you simplify it at all (e.g. run <strong>.Reduce</strong> in SQL server or <strong>ST_Simplify</strong> in Postgres).&nbsp; We have seen numerous datasets where there are many nodes per metre and this is just extra load if the data is simply going to be displayed against a tiled map so its worth considering if its worth creating&nbsp;&nbsp; simplified version of your data.</p>

<h2>What is the maximum zoom I want users to see my data</h2>

<p>Its pointless to allow users to zoom in past the appropriate resolution for your data, and as you will see when we build the cache each extra step requires 4 times the storage of the previous level. <a href="http://bboxfinder.com" title="http://bboxfinder.com">http://bboxfinder.com</a> provides an easy way to check which tile step you are viewing the map at – and decide what is appropriate for your data.&nbsp;&nbsp; Tile steps run from 0 to 19 normally.</p>

<h2>What is the update frequency / churn of my data</h2>

<p>Caching is relatively easy if the data is unchanging.&nbsp; If it changes frequently you need to think about how to manage cache refreshing and what is the maximum time that users can wait to see refreshed data.&nbsp; As always there is a trade off with performance here.</p>

<h2>Do users filter my data</h2>

<p>If users filter your data to display you have to decide if you will cache every possible permutation (which may well not be practical unless the filters are very limited) or just cache the base layer and accept users will hit the source for filtered views.</p>

<h1>Deciding what to cache</h1>

<p>Once you have decided you want to cache your layer you then need to decide if you are going to cache every zoom level you are displaying the data at – or perhaps just the lower zoom levels and let users hit the source for detailed views (which will reduce your cache storage significantly in some cases)</p>

<h1>Enabling caching in geoserver</h1>

<p>The easiest way to enable caching (in that you can just request your WMS as normal) is to <em>enable direct integration with Geoserver WMS</em>&nbsp; on the <strong>Tile Caching, Caching defaults</strong> menu.&nbsp; This is not set on a default install.</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_153.png"><img alt="image" border="0" height="106" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_thumb_124.png" style="margin: 0px; display: inline; background-image: none;" title="image" width="244" /></a></p>

<h1>Setting up disk quota</h1>

<p>This is an important setting.&nbsp; A pre-seeded cache will take up a lot of disk space and you want to make sure you have enough space set aside.&nbsp; By default the cache will go in your \geoserver\gwc folder</p>

<p>If you do not have limitless disk space its probably worth setting a quota appropriate to your infrastructure.</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_154.png"><img alt="image" border="0" height="219" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_thumb_125.png" style="margin: 0px; display: inline; background-image: none;" title="image" width="244" /></a></p>

<h1>Setting up caching on individual layers</h1>

<p>You must then enable caching on the layer that you want to cache using the <strong>Tile caching, Tile Layers</strong> menu.</p>

<p>Pick the layers that you want to cache and then click <strong>Configure selected layers with caching defaults</strong></p>

<p>There are a few settings we need to consider in here:</p>

<h2>Data tab</h2>

<h3>Bounding boxes</h3>

<p>If these are computed from the SRS bounds make sure they cover no more than the area you want cached. <a href="http://bboxfinder.com" title="http://bboxfinder.com">http://bboxfinder.com</a> provides an easy way do get coordinates for a custom bounding box.</p>

<h2>Publishing tab</h2>

<h3>HTTP Settings</h3>

<p>This allows you to set the headers in the response to the client browser and control how long the browser itself should cache the tile – so not needing to request even the server cached tile again.</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_155.png"><img alt="image" border="0" height="72" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_thumb_126.png" style="margin: 0px; display: inline; background-image: none;" title="image" width="244" /></a></p>

<h2>Tile caching tab</h2>

<h2>&nbsp;</h2>

<h3>Tile image formats</h3>

<p>Correct setting here can make massive difference to your cache size.&nbsp; If you are in control of the client you might want to restrict caching to just one file format.&nbsp; We tend to use png8 which is normally 25% of the size of png and allows transparency.</p>

<h3>Metatiling factors</h3>

<p>By default this is 4 x 4 which means when building the cache it will request a tile 4 times the size you actually want – this helps with avoiding chopped labels on the edge etc</p>

<h3>Cache expiry</h3>

<p>0 means use the server default (which is normally off I think).</p>

<p>-1 means switch off (i.e. never), any other value is the number of seconds till the cache expires</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_156.png"><img alt="image" border="0" height="65" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_thumb_127.png" style="margin: 0px; display: inline; background-image: none;" title="image" width="244" /></a></p>

<p>The server cache setting determines how old a cached tile can be before it needs to be regenerated from source.&nbsp; If your data is static you can leave this setting off.&nbsp; If it changes you will need to put some thought into how old its acceptable for data to appear to clients.</p>

<p>The client cache is a little less clear it seems to overlap with the HTTP settings (above) though has been reported to not always work.&nbsp; It may not be needed if you have set the HTTP header response.</p>

<h3>Zoom levels</h3>

<p>You need to decide what zoom levels are appropriate for your data and whether you want them all cached.&nbsp; Sometimes we don’t cache the most detailed layer as that significantly reduces the cache size.</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_157.png"><img alt="image" border="0" height="31" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_thumb_128.png" style="margin: 0px; display: inline; background-image: none;" title="image" width="244" /></a></p>

<h2>Seeding the cache</h2>

<p>By default having made all the settings above your cache will start to be built.&nbsp; Every time a WMS request is received by GWC the cache will be checked first, it it doesn’t exist in there the tile will be generated and saved in the cache for subsequent requests.&nbsp; As you can imagine the performance of your service will be variable as the cache is randomly built up over time.</p>

<p>Alternatively you can go to <a href="https://mygeoserverurl/geoserver/gwc/demo">https://<em>mygeoserverurl</em>/geoserver/gwc/demo</a> and seed the cache.</p>

<p>Select your layer and decide how many tasks you want to run, the zoom levels you want to seed and the bounding box you want to seed within.&nbsp; Then set it running.</p>

<p>Note:&nbsp; For detailed zoom levels this will take many hours / days and use potentially TB of disk space</p>

<h1>Testing your cache</h1>

<p>Hopefully you will see a noticeable performance gain when the cache is being hit, but there are a couple of checks you can do in a client browser.</p>

<p>First of all though – remember you cache will only be used if:</p>

<ul>
	<li>Your application is requesting WMS tiles</li>
	<li>The call includes &amp;TILES=True as part of the call</li>
</ul>

<p>If you open the dev tools in your browser (we are using chrome here – but firefox and IE have similar) and inspect a call you are making to geoserver</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_158.png"><img alt="image" border="0" height="185" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_thumb_129.png" style="display: inline; background-image: none;" title="image" width="244" /></a></p>

<p>There are several things you can see from the headers:</p>

<ul>
	<li><strong>Status code 200</strong> – this means that the call was made (as opposed to locally cached)</li>
	<li><strong>geowebcache-cache-result: HIT</strong> – this tells you that your tile was found in the cache.</li>
</ul>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_159.png"><img alt="image" border="0" height="147" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_thumb_130.png" style="margin: 0px; display: inline; background-image: none;" title="image" width="244" /></a></p>

<p>If you refresh the page you case see:</p>

<ul>
	<li><strong>Status code 304</strong> – this shows that the tile was retrieved from your local browser cache.</li>
	<li>Because it came from your local cache there is no geowebcache value.</li>
</ul>
<br /><a href='https://www.esdm.co.uk/configuring-geoserver-with-geo-web-cache-gwc'></a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/configuring-geoserver-with-geo-web-cache-gwc'>...</a>]]></description>
      <link>https://www.esdm.co.uk/configuring-geoserver-with-geo-web-cache-gwc</link>
      <author>()</author>
      <comments>https://www.esdm.co.uk/configuring-geoserver-with-geo-web-cache-gwc</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/configuring-geoserver-with-geo-web-cache-gwc</guid>
      <pubDate>Thu, 21 Feb 2019 17:46:00 GMT</pubDate>
    </item>
    <item>
      <title>Mapserver returning WFS attributes scrambled when using msplugin_mssql2008 plugin</title>
      <description><![CDATA[<p>We recently upgraded Mapserver to the current version for a project because we wanted the WFS id field to be populated in geojson which required a recent version of (OGR &gt;=2.3) which was included in mapserver build 7.2.1</p><p>All went well and we modified the mapserver geojson output format to specify the field we wanted to use (originally named id in our case) but adding in the line: FORMATOPTION "LCO:ID_FIELD=id"</p><blockquote><p>OUTPUTFORMAT<br>&nbsp;&nbsp; NAME "geojson"<br>&nbsp;&nbsp; DRIVER "OGR/GEOJSON"<br>&nbsp;&nbsp; MIMETYPE "application/json; subtype=geojson"<br>&nbsp;&nbsp; FORMATOPTION "STORAGE=stream"<br>&nbsp;&nbsp; FORMATOPTION "FORM=SIMPLE"<br>&nbsp;&nbsp; FORMATOPTION "LCO:COORDINATE_PRECISION=0"<br>&nbsp;&nbsp; FORMATOPTION "LCO:ID_FIELD=id"<br>
END</p></blockquote><p>The field “id” needed to be included in the relevant map layer in the gml_include_items e.g.:</p><blockquote><p> "gml_include_items"&nbsp;&nbsp;&nbsp;&nbsp; "name,recsubtype,recsubtypecode,rectype,ref,uid,id"</p></blockquote><p>After those changes – all seemed to be going well and the id was appearing in our WFS return</p><p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_151.png"><img width="244" height="52" title="image" style="margin: 0px; display: inline; background-image: none;" alt="image" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_thumb_122.png" border="0"></a></p><p>However when we looked at some of the other attributes we noticed scrambled characters (these had been fine with the previous version of mapserver). e.g. rectype in screenshot below</p><p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_152.png"><img width="244" height="57" title="image" style="margin: 0px; display: inline; background-image: none;" alt="image" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_thumb_123.png" border="0"></a></p><p>Our data was coming from SQL server using the msplugin_mssql2008 plugin– and if we set debug in the mapserver layer and looked at the log we saw:</p><blockquote><p><em>msConvertWideStringToUTF8(): General error message. Unable to convert string in encoding 'UCS-2LE' to UTF8 An invalid multibyte sequence has been encountered in the input</em></p></blockquote><p>So it looked as it something was going wrong in the unicode translation.&nbsp; The workaround was refreshingly simple – we just had to alter the view that the data was being pulled from to force the conversion e.g. change:</p><blockquote><p>‘Site’</p><p>to:</p><p>CONVERT(nvarchar(1),N’Site’)</p></blockquote><p>then all was well again.</p><br /><a href='https://www.esdm.co.uk/mapserver-returning-wfs-attributes-scrambled-when-using-msplugin_mssql2008-plugin'></a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/mapserver-returning-wfs-attributes-scrambled-when-using-msplugin_mssql2008-plugin'>...</a>]]></description>
      <link>https://www.esdm.co.uk/mapserver-returning-wfs-attributes-scrambled-when-using-msplugin_mssql2008-plugin</link>
      <author>()</author>
      <comments>https://www.esdm.co.uk/mapserver-returning-wfs-attributes-scrambled-when-using-msplugin_mssql2008-plugin</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/mapserver-returning-wfs-attributes-scrambled-when-using-msplugin_mssql2008-plugin</guid>
      <pubDate>Thu, 21 Feb 2019 12:54:32 GMT</pubDate>
    </item>
    <item>
      <title>Using cloudscribe with Identity Server 4 with a SQL Server store on .Net Core 2 MVC securing APIs</title>
      <description><![CDATA[<p>Well that’s a bit of a mouthful of a title – but it does describe what I was trying to do, and I didn’t find it easy. It’s not that complicated when you know how, but there are an awful lot of options and it wasn’t clear (to me at least) which ones to pick, so I hope this saves others some time.</p>

<h4>Aim</h4>

<p>I wanted a <a href="https://www.cloudscribe.com/">cloudscribe</a> site running allowing users to be able to logon as usual using cookies, but I also wanted to have an API that could be secured for clients external to the site without having to use the cookie login and I wanted to use Identity Server 4 with a SQL data store.</p>

<h3>Starting setup</h3>

<h4>Server</h4>

<p>I used Joe Audette's excellent cloudscribe Visual Studio template <a href="https://www.cloudscribe.com/blog/2017/09/11/announcing-cloudscribe-project-templates">https://www.cloudscribe.com/blog/2017/09/11/announcing-cloudscribe-project-templates</a> and selected use MSSQL Server and the Include Identity Server integration option. Also selecting the 2 options in the “Expert Zone” gave me an example API to test with.</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image002_8.jpg"><img alt="clip_image002" border="0" height="244" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image002_thumb_7.jpg" style="margin: 0px; display: inline; background-image: none;" title="clip_image002" width="227" /></a></p>

<p>This gave me the basic website and was the one I wanted to add the secured API into. The VS project has a weather forecast API as an example.</p>

<h4>Client</h4>

<p>I then setup a separate MVC project using a basic template to act as the client application. This was all done using Visual Studio 2017 and .Net Core 2.</p>

<h3>Server application</h3>

<p>By default, the weather forecast API is accessible to all users. Try: http://localhost:35668/api/SampleData/WeatherForecasts</p>

<p>You can secure this by adding the [Authorize] statement to the API on the SampleDataController.cs page e.g.</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
[Authorize]
[HttpGet(<span style="color: rgb(0, 96, 128);">"[action]"</span>)]
<span style="color: rgb(0, 0, 255);">public</span> IEnumerable&lt;WeatherForecast&gt; WeatherForecasts()
{
    var rng = <span style="color: rgb(0, 0, 255);">new</span> Random();</pre>
</div>

<p>but you will find this presents the standard cloudscribe logon screen to access it – not exactly what’s wanted for an API.</p>

<p>In order to solve this we need to use JWT authorisation alongside the standard cookie authentication, but tell the API to only secure using the JWT authorisation . This is done by filtering the authentication scheme used by the Authorize statement as below (you will probably have to add the following assemblies to your code)</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
<span style="color: rgb(0, 0, 255);">using</span> Microsoft.AspNetCore.Authorization;
<span style="color: rgb(0, 0, 255);">using</span> Microsoft.AspNetCore.Authentication.JwtBearer;</pre>
</div>

<p>and then add the filter to the authorize statement.</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[HttpGet(<span style="color: rgb(0, 96, 128);">"[action]"</span>)]
<span style="color: rgb(0, 0, 255);">public</span> IEnumerable&lt;WeatherForecast&gt; WeatherForecasts()
{
    var rng = <span style="color: rgb(0, 0, 255);">new</span> Random();</pre>
</div>

<p>We have now told the API to authenticate using JWTBearer but we haven’t yet added JWT authentication to our applications pipeline. So in the startup.cs page we need to add in some assemblies:</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
<span style="color: rgb(0, 0, 255);">using</span> Microsoft.AspNetCore.Authentication.JwtBearer;</pre>
</div>

<p>and then add the JWT service into the ConfigureServices method. (I added the statement below just above services.AddCors(options =&gt;)</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
services.AddAuthentication(options =&gt;
{
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})

.AddJwtBearer(options =&gt;
{
    options.Authority = <span style="color: rgb(0, 96, 128);">"http://localhost:35668"</span>; <span style="color: rgb(0, 128, 0);">//No trailing /</span>
    options.Audience = <span style="color: rgb(0, 96, 128);">"api2"</span>; <span style="color: rgb(0, 128, 0);">//Name of api</span>
    options.RequireHttpsMetadata = <span style="color: rgb(0, 0, 255);">false</span>;

});</pre>
</div>

<p>Where:</p>

<p>.Authority is the address of the website with the api (note no trailing slash)</p>

<p>.Audience is the name you have given to the api in the Identity server 4 security setup (see more details below)</p>

<p>And then we need to tell our pipeline to use Authentication. So add the app.UseAuthentication() into the end of the ConfigureServices method just above the UseMVC call</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
app.UseAuthentication();
UseMvc(app, multiTenantOptions.Mode == cloudscribe.Core.Models.MultiTenantMode.FolderName);</pre>
</div>

<p>Now if you try and access the api -&nbsp;http://localhost:35668/api/SampleData/WeatherForecasts&nbsp;- you should get an unauthorised message - even if you are logged onto the cloudscribe site using cookie authentication.</p>

<h3>Identity Server 4 configuration (through cloudscribe)</h3>

<p>Identity server has many options – which can be bewildering to start with. Full documentation is here: <a href="https://identityserver4.readthedocs.io/">https://identityserver4.readthedocs.io/</a></p>

<p>For our purposes here – I’m outlining the bare minimum that we need to setup security for our API, either using:</p>

<p>· A client credential using a secret</p>

<p>· A username and password</p>

<h4>API resources</h4>

<p>Under the admin menu in cloudscribe select security settings / API resources and create a new API record giving it a name (e.g. api2) making sure it matches the name you entered as the .Audience in the startup.cs .</p>

<p>Then we need to add a single scope record – called “allowapi2” in this example.</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image004_5.jpg"><img alt="clip_image004" border="0" height="155" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image004_thumb_5.jpg" style="margin: 0px; display: inline; background-image: none;" title="clip_image004" width="244" /></a></p>

<h4>Client resources</h4>

<p>Under the admin menu in cloudscribe select security settings / API Clients and create a new client (I’ve called it client2 – remember this name for when we make the call from the client application). Edit the new client record and add:</p>

<p>· Allowed Scope record – e.g. allowapi2 – this must match the scope we entered for the api and is used to specify which apis this client can access</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image006_3.jpg"><img alt="clip_image006" border="0" height="86" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image006_thumb_3.jpg" style="margin: 0px; display: inline; background-image: none;" title="clip_image006" width="214" /></a></p>

<p>· Client Secrets – the value is the SHA56 value of the secret we wish to use (in this example secret2) <s>– at the moment the cloudscribe interface doesn’t do this conversion for us so we have to do it manually somewhere&nbsp;(e.g. I used string s = "secret2".ToSha256();)&nbsp; &nbsp;</s></p>

<p><s>I added the secret using the web page and then pasted the converted secret direct into the relevant field in the record in the csids_ClientSecrets table in the database - but I think it would work equally well just pasting the converted value into the web page.</s></p>

<p><s>.ToSha256() is a string extension method&nbsp;in the IdentityModel assembly - this seems to do more than simply convert to sha256 - see&nbsp;<a href="https://github.com/IdentityModel/IdentityModel/blob/master/source/IdentityModel.Net45/Extensions/HashStringExtensions.cs">https://github.com/IdentityModel/IdentityModel/blob/master/source/IdentityModel.Net45/Extensions/HashStringExtensions.cs</a>.</s></p>

<p><s>It’s important that we set the secret type as well – in our example here it must be “SharedSecret”</s></p>

<p>Joe Audette has updated his nuget packages so saving a client secret now gives you a range of options for the secret type - in our example we need to pick "SharedSecret" and select to encrypt using Sha256 (see Joe's post in comments below for other options) which should make things easier.</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image008_1.jpg"><img alt="clip_image008" border="0" height="68" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image008_thumb_1.jpg" style="margin: 0px; display: inline; background-image: none;" title="clip_image008" width="244" /></a></p>

<p>· Allowed Grant types – we are entering “password” and “client_credentials”. These determine how we can authenticate from the client app as we see below in the next section. Password means that authentication can use a username / pwd combination (i.e. a cloudscribe login). Client_credentials means we can login using a client secret and don’t have to be a known user on the site.</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image010_1.jpg"><img alt="clip_image010" border="0" height="81" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image010_thumb_1.jpg" style="display: inline; background-image: none;" title="clip_image010" width="200" /></a></p>

<h3>Client application</h3>

<p>To connect securely to the API using a client connection with a secret use:</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
var tokenClient = <span style="color: rgb(0, 0, 255);">new</span> TokenClient(disco.TokenEndpoint, <span style="color: rgb(0, 96, 128);">"client2"</span>, <span style="color: rgb(0, 96, 128);">"secret2"</span>);</pre>
</div>

<p>To connect using a username and password use:</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
var tokenResponsePassword = await tokenClient.RequestResourceOwnerPasswordAsync(<span style="color: rgb(0, 96, 128);">"admin"</span>, <span style="color: rgb(0, 96, 128);">"admin"</span>, <span style="color: rgb(0, 96, 128);">"allowapi2"</span>);</pre>
</div>

<p>Note that the user name is the user name not the email which can be used to login interactively.</p>

<p>The whole method in the controller looked something like this – the rest of the code is deserializing the JSON return from the API and putting it into an object that can be displayed on a view page</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
<span style="color: rgb(0, 0, 255);">using</span> System;
<span style="color: rgb(0, 0, 255);">using</span> System.Collections.Generic;
<span style="color: rgb(0, 0, 255);">using</span> System.Diagnostics;
<span style="color: rgb(0, 0, 255);">using</span> System.Threading.Tasks;
<span style="color: rgb(0, 0, 255);">using</span> Microsoft.AspNetCore.Mvc;
<span style="color: rgb(0, 0, 255);">using</span> ESDM.Models;
<span style="color: rgb(0, 0, 255);">using</span> System.Net.Http;
<span style="color: rgb(0, 0, 255);">using</span> Newtonsoft.Json;
<span style="color: rgb(0, 0, 255);">using</span> IdentityModel.Client;
<span style="color: rgb(0, 0, 255);">using</span> IdentityServerClient.Models;
<span style="color: rgb(0, 0, 255);">using</span> IdentityModel;</pre>
</div>

<p>and then</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
<span style="color: rgb(0, 128, 0);">//Hosted web API REST Service base url  </span>
<span style="color: rgb(0, 0, 255);">string</span> Baseurl = <span style="color: rgb(0, 96, 128);">"http://localhost:35668"</span>;

<span style="color: rgb(0, 0, 255);">public</span> async Task&lt;ActionResult&gt; Index()
{
    List&lt;WeatherForecast&gt; ans = <span style="color: rgb(0, 0, 255);">new</span> List&lt;WeatherForecast&gt;();
     <span style="color: rgb(0, 0, 255);">using</span> (var client = <span style="color: rgb(0, 0, 255);">new</span> HttpClient())
    {
        <span style="color: rgb(0, 128, 0);">// discover endpoints from metadata</span>
        var disco = await DiscoveryClient.GetAsync(Baseurl);
        var tokenClient = <span style="color: rgb(0, 0, 255);">new</span> TokenClient(disco.TokenEndpoint, <span style="color: rgb(0, 96, 128);">"client2"</span>, <span style="color: rgb(0, 96, 128);">"secret2"</span>);
        var tokenResponse = await tokenClient.RequestClientCredentialsAsync(<span style="color: rgb(0, 96, 128);">"allowapi2"</span>);

<span style="color: rgb(0, 128, 0);">//Example getting alternative token if you want to use username / pwd </span>
        var tokenResponsePassword = await tokenClient.RequestResourceOwnerPasswordAsync(<span style="color: rgb(0, 96, 128);">"admin"</span>, <span style="color: rgb(0, 96, 128);">"admin"</span>, <span style="color: rgb(0, 96, 128);">"allowapi2"</span>);

        <span style="color: rgb(0, 128, 0);">// call api - change for tokenResponsePassword if you want to use username / pwd</span>
        client.SetBearerToken(tokenResponse.AccessToken);

        var response = await client.GetAsync(Baseurl + <span style="color: rgb(0, 96, 128);">"/api/SampleData/WeatherForecasts"</span>);
        <span style="color: rgb(0, 0, 255);">if</span> (response.IsSuccessStatusCode)
        {
            var content =  response.Content.ReadAsStringAsync().Result;
            ans = JsonConvert.DeserializeObject&lt;List&lt;WeatherForecast&gt;&gt;(content);
        }
        <span style="color: rgb(0, 0, 255);">return</span> View(ans);
    }</pre>
</div>

<p>The model for the forecast data was:</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
<span style="color: rgb(0, 0, 255);">namespace</span> ESDM.Models
{
    <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">partial</span> <span style="color: rgb(0, 0, 255);">class</span> WeatherForecast
    {
        <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">string</span> DateFormatted { get; set; }
        <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> TemperatureC { get; set; }
        <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">string</span> Summary { get; set; }

        <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> TemperatureF
        {
            get
            {
                <span style="color: rgb(0, 0, 255);">return</span> 32 + (<span style="color: rgb(0, 0, 255);">int</span>)(TemperatureC / 0.5556);
            }
        }
    }
}
</pre>
</div>

<p>And my view contained</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
@model IEnumerable&lt;ESDM.Models.WeatherForecast&gt;
&lt;div&gt;
    &lt;ul&gt;
        @<span style="color: rgb(0, 0, 255);">foreach</span> (var forecast <span style="color: rgb(0, 0, 255);">in</span> Model)
        {
            &lt;li&gt;@forecast.Summary&lt;/li&gt;
        }
    &lt;/ul&gt;
&lt;/div&gt;</pre>
</div>
<br /><a href='https://www.esdm.co.uk/using-cloudscribe-with-identity-server-4-with-a-sql-server-store-on-net-core-2-mvc-securing-apis'></a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/using-cloudscribe-with-identity-server-4-with-a-sql-server-store-on-net-core-2-mvc-securing-apis'>...</a>]]></description>
      <link>https://www.esdm.co.uk/using-cloudscribe-with-identity-server-4-with-a-sql-server-store-on-net-core-2-mvc-securing-apis</link>
      <author>()</author>
      <comments>https://www.esdm.co.uk/using-cloudscribe-with-identity-server-4-with-a-sql-server-store-on-net-core-2-mvc-securing-apis</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/using-cloudscribe-with-identity-server-4-with-a-sql-server-store-on-net-core-2-mvc-securing-apis</guid>
      <pubDate>Fri, 03 Nov 2017 22:14:00 GMT</pubDate>
    </item>
    <item>
      <title>How to link SQL Server / RDBMS tables into MS Access without a DSN</title>
      <description><![CDATA[<p>Linking tables from RDBMS like SQL Server into an Access front end doesn't need an ODBC DSN to be configured, it can be done with a one-liner. I’m just posting this here because I keep having to look it up and can never&nbsp;find it.<br />
This example is for SQL Server; change the driver for other databases. Run this from the immediate window or a macro (adjust server, table names and credentials to suit):</p>

<p><font face="Courier New">docmd.TransferDatabase acLink, "ODBC","ODBC;DRIVER=SQL Server;SERVER=myServer\myInstanceIfNeeded;DATABASE=myDatabaseName;Trusted_connection=yes;", acTable,"myTableName","myTableName",False,True</font></p>

<p>The final parameter ensures that login credentials are stored.</p>

<p>Of course our&nbsp;production applications&nbsp;like HBSMR and PACS manage&nbsp;database connections&nbsp;in a more sophisticated manner, with the ability to switch databases and define which tables/views are attached in one operation, but the above technique can be useful where MS Access is being used for&nbsp;quick querying/editing/reporting operations.</p>
<br /><a href='https://www.esdm.co.uk/how-to-link-sql-server--rdbms-tables-into-ms-access-without-a-dsn'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/how-to-link-sql-server--rdbms-tables-into-ms-access-without-a-dsn'>...</a>]]></description>
      <link>https://www.esdm.co.uk/how-to-link-sql-server--rdbms-tables-into-ms-access-without-a-dsn</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/how-to-link-sql-server--rdbms-tables-into-ms-access-without-a-dsn</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/how-to-link-sql-server--rdbms-tables-into-ms-access-without-a-dsn</guid>
      <pubDate>Thu, 30 Mar 2017 18:24:00 GMT</pubDate>
    </item>
    <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>GeoServer WFS fails on SQL Server tables/views with GUID data type</title>
      <description><![CDATA[<p>Just a quick note that might be useful to some.</p>

<h4>The problem...</h4>

<p>If you publish a SQL Server (in this case 2014) table or view with a GUID field using GeoServer (version&nbsp;2.10), it will fail to make&nbsp; a valid WFS because the &amp;request=DescribeFeatureType response <em>will not include these layers</em>.</p>

<p>This manifests itself in various ways depending on the client, but in QGIS on trying to load the layer into the map we get this in the "Log Messages Panel":</p>

<blockquote>
<p>Analysis of DescribeFeatureType response failed for url&nbsp; srsname='EPSG:27700' typename=xxx:yyyurl='https://mysite.com/myworkspace/wfs' version='1.0.0' table="" sql=: it is probably a schema for Complex Features</p>
</blockquote>

<p>which had me puzzled for quite some time!</p>

<h4>The solution...</h4>

<p>Changing the data source view to include this field with</p>

<p>CAST(myGUID AS varchar(36)) AS myGUID</p>

<p>then hitting "Reload feature type" in the GeoServer layer configuration screen fixes it. This changes the field data type from "UUID" to "string" in that screen. Or just omit the field entirely.</p>

<p>My guess is this will not help transactional WFS, but I have not tested this. Overall, GUIDs are still best avoided in GIS-land as they are so poorly supported by the database drivers.</p>
<br /><a href='https://www.esdm.co.uk/geoserver-wfs-fails-on-sql-server-tablesviews-with-guid-data-type'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/geoserver-wfs-fails-on-sql-server-tablesviews-with-guid-data-type'>...</a>]]></description>
      <link>https://www.esdm.co.uk/geoserver-wfs-fails-on-sql-server-tablesviews-with-guid-data-type</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/geoserver-wfs-fails-on-sql-server-tablesviews-with-guid-data-type</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/geoserver-wfs-fails-on-sql-server-tablesviews-with-guid-data-type</guid>
      <pubDate>Wed, 11 Jan 2017 22:23:00 GMT</pubDate>
    </item>
    <item>
      <title>A regular expression for numbers from 0 to 100</title>
      <description><![CDATA[<p>I've been creating a web questionnaire (using the Forms Wizard module for mojoPortal CMS) that includes some questions where the answers were to be expressed as integer percentages. How to validate the responses? This was not quite as simple as I was hoping, as we need to use a RegularExpressionValidator and, as&nbsp;any fule kno<em>,</em> using regular expressions to solve a problem usually results in having two problems. So what's the expression? Of course regular expressions don't "understand" numbers,&nbsp;so&nbsp;it's not as simple as ^[0-100]$!</p>

<p>This expression seems to do the job without loopholes: ^([0-9]|[1-9][0-9]|100)$</p>

<p>If we want to permit decimals, then it gets more interesting. I'm no regexpert, but something like this should&nbsp;ensure we get either an integer from 0 to 100 or any decimal in between, with up to 2d.p. (also permitting 100.0 and 100.00!):</p>

<p>^100(\.[0]{1,2})?|([0-9]|[1-9][0-9])(\.[0-9]{1,2})?$</p>

<p>Spot any problems with these regular expressions...? Please comment and help me improve!</p>
<br /><a href='https://www.esdm.co.uk/a-regular-expression-for-numbers-from-0-to-100'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/a-regular-expression-for-numbers-from-0-to-100'>...</a>]]></description>
      <link>https://www.esdm.co.uk/a-regular-expression-for-numbers-from-0-to-100</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/a-regular-expression-for-numbers-from-0-to-100</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/a-regular-expression-for-numbers-from-0-to-100</guid>
      <pubDate>Fri, 29 Jul 2016 17:55:00 GMT</pubDate>
    </item>
    <item>
      <title>How to change the port for GeoServer on Windows with Jetty</title>
      <description><![CDATA[<p>Faced with the task of upgrading a production GeoServer under heavy constant use, I decided to install the newer version alongside first, migrate the content, then make the switch when I was happy everything was working. I installed the new GeoServer 2.7.0 using the Windows installer, choosing port 8081 (the old version was running on port 8080), and electing to run it as a service. I cloned the data directory and pointed the new version at the new copy. Once the SQL Server extension was installed, everything appeared to be working fine – all layers working well. The only problem I could see was the the disk quota functionality was failing with visible warning messages in the GeoServer admin interface, and I guessed that this was probably because two different versions could not use the same disk quota database (this turned out to be correct).</p>

<p>The next step was to switch off the old version of GeoServer and present the new version on its address, minimising transition time. This required changing the port of the new version from 8081 to 8080, so I scoured the internet on how to do this. I found a few threads out there, but none gave me the right answer. They specifically mentioned \etc\jetty.xml and GeoServer’s startup.bat, neither of which configured the port for my installation. In fact the configuration is to be found against the Windows service:</p>

<p><img alt="GeoServer Windows service" border="0" height="427" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_143.png" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" title="GeoServer Windows service" width="644" /></p>

<p>Opening this service reveals the port specified in the command line (here showing after I had changed it):</p>

<p><img alt="GeoServer service commandline" border="0" height="465" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_144.png" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" title="GeoServer service commandline" width="414" /></p>

<p style="width:100%; white-space: pre-wrap;">Changing this command required editing the registry, with these settings found under HKEY_LOCAL_MACHINE\SYSTEM\ ControlSet001\services\GeoServer 2.7.0:</p>

<p><img alt="GeoServer service registry settings" border="0" height="272" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_145.png" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" title="GeoServer service registry settings" width="644" /></p>

<p>Edit the command-line as follows:</p>

<p><img alt="GeoServer service command" border="0" height="166" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_146.png" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" title="GeoServer service command" width="388" /></p>

<p>I also edited the previous version to start on port 8081 (in case I needed to run both), then stopped the older version’s service and restarted the new version’s service. Everything worked fine, with an imperceptible transition. And as predicted, the disk quota thing started working too.</p>

<p>So that’s how to change the port for GeoServer running on Windows with Jetty!</p>
<br /><a href='https://www.esdm.co.uk/how-to-change-the-port-for-geoserver-on-windows-with-jetty'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/how-to-change-the-port-for-geoserver-on-windows-with-jetty'>...</a>]]></description>
      <link>https://www.esdm.co.uk/how-to-change-the-port-for-geoserver-on-windows-with-jetty</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/how-to-change-the-port-for-geoserver-on-windows-with-jetty</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/how-to-change-the-port-for-geoserver-on-windows-with-jetty</guid>
      <pubDate>Sun, 19 Apr 2015 13:23:00 GMT</pubDate>
    </item>
    <item>
      <title>Troubleshooting issues with VSS from backup chain and SQL Server</title>
      <description><![CDATA[<h2>Symptoms:</h2> <p>BackupChain fails almost as soon as it starts when backing up SQL Server VM</p> <p>SQL server VM Application log is riddled with errors</p> <h2>Diagnostics:</h2> <h3>Use VSSDiag</h3> <p>You can start by using the VssDiag <a href="http://backupchain.com/VssDiag.html">http://backupchain.com/VssDiag.html</a> and enter the exact time when the error was reported by backup chain(within 5 minutes) on the SQL Server VM</p> <p>This will list any VSS issues from the log</p> <p>I saw an error like:</p> <blockquote> <p><em>A VSS writer has rejected an event with error 0x800423f4, The writer experienced a non-transient error.&nbsp; If the backup process is retried,<br>the error is likely to reoccur.<br>. Changes that the writer made to the writer components while handling the event will not be available to the requester.<br>Check the event log for related events from the application hosting the VSS writer.</em></p> <p><br><em>Operation:<br>&nbsp;&nbsp; PrepareForSnapshot Event</em></p> <p><em>Context:<br>&nbsp;&nbsp; Execution Context: Writer<br>&nbsp;&nbsp; Writer Class Id: {a65faa63-5ea8-4ebc-9dbd-a0c4db26912a}<br>&nbsp;&nbsp; Writer Name: SqlServerWriter<br>&nbsp;&nbsp; Writer Instance Name: SQL Server 2008 R2:SQLWriter<br>&nbsp;&nbsp; Writer Instance ID: {d78d914d-4a11-4350-8334-af08bff3ce07}<br>&nbsp;&nbsp; Command Line: "C:\Program Files\Microsoft SQL Server\90\Shared\sqlwriter.exe"<br>&nbsp;&nbsp; Process ID: 2408</em></p></blockquote> <h3>Look in VM Application Event Log</h3> <p>I saw several entries for databases:</p> <blockquote> <p><em>The log for database 'CMSi-TNC-demo' is not available. Check the event log for related error messages. Resolve any errors and restart the database.</em></p></blockquote> <p>If in Enterprise manager you run DBCC CHECKDB&nbsp; for the database mentioned you may see a host of errors</p> <h2>Fix:</h2> <p>I found the fix was to take the database in question&nbsp; offline and then put it back online again.</p> <p>Running DBCC CHECKDB again then reported no errors</p> <p>After I had worked through all the databases that had this error – BackupChain could then run a VM backup</p><br /><a href='https://www.esdm.co.uk/troubleshooting-issues-with-vss-from-backup-chain-and-sql-server'></a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/troubleshooting-issues-with-vss-from-backup-chain-and-sql-server'>...</a>]]></description>
      <link>https://www.esdm.co.uk/troubleshooting-issues-with-vss-from-backup-chain-and-sql-server</link>
      <author>()</author>
      <comments>https://www.esdm.co.uk/troubleshooting-issues-with-vss-from-backup-chain-and-sql-server</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/troubleshooting-issues-with-vss-from-backup-chain-and-sql-server</guid>
      <pubDate>Thu, 12 Mar 2015 09:12:25 GMT</pubDate>
    </item>
    <item>
      <title>How to do a typeahead incremental search control in Microsoft Access</title>
      <description><![CDATA[<p>When a user needs to find an existing record or value by typing something in a box, it is well recognized that a <a href="http://en.wikipedia.org/wiki/Incremental_search" target="_blank">"typeahead" or incremental search</a> is a powerful and user-friendly solution.</p>

<p>Of course MS Access has featured combo boxes (drop-down lists) with “auto expand” capabilities since time began; here’s an example where I’ve typed “pen” into the control and it has automatically selected the first matching value:</p>

<p><img alt="Standard Auto Expand in an Access combo box" border="0" height="281" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_140.png" style="border-width: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Standard Auto Expand in an Access combo box" width="415" /></p>

<p>(The list can be opened by clicking on the down arrow, or by hitting F4 - one of the many keyboard shortcuts that nobody knows - or in code with the dropdown method).</p>

<p>This is fine for most situations, but there are cases where the auto-expanding combo box falls down:</p>

<ul>
	<li>Very long lists (like many hundreds or thousands of rows in the drop-down): firstly this can be a performance problem, putting significant load on network and database whenever a user uses the control, secondly very long lists are not user-friendly, and worst of all the auto expand can just stop working or give strange results; in a system I was working on with SQL Server data storage, one combo with &gt;4000 rows would frequently fail to find values in the list when the user started typing.</li>
	<li>Lists where the user may not know the starting text (e.g. trying to find Southampton in a list that includes it as City of Southampton).</li>
	<li>Situations where we want to give the user an easy way of selecting multiple items. Not the main focus of this post, but it does provide much of a solution if you need it.</li>
</ul>

<p>This post describes a way of providing a nice user experience in these situations.</p>

<p>In the first simple example implementation the user is looking for the record for one of our clients based on knowing the unusual first name of a member of staff there. In this case the dropdown opens when the user has typed “bri”, showing lots of rows of people called “Bridges” etc, then when the user adds a “t” the list looks like this:</p>

<p><img alt="Incremental search in an Access combo box" border="0" height="147" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_141.png" style="border: 0px currentColor; border-image: none; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Incremental search in an Access combo box" width="589" /></p>

<p>On selecting a row, the form updates to show the full record for the selected client.</p>

<p>An implementation like this starts with an unbound combo box, making sure its Auto Expand property is set to No. You will ultimately need to clear the Row Source, but it makes design easier to start with a row source for the dropdown that closely resembles the final version. Typically this might be a table with a primary key value that will be hidden (e.g. an ID), and a visible descriptive field (e.g. a Name). Other properties can be set to suit requirements, so perhaps (following this simple example) Column Count = 2, Column Widths “0cm;10 cm” and List Width “10cm”. Keep Limit To List set to “Yes”. When you have a combo box that is behaving nicely with a Row Source, switch back to design view and remove the Row Source. Leave the Row Source Type set to “Table/Query”.</p>

<p>Now we need to add some code to make it work. First we add a handler for the On Key Down event, like this:</p>

<p><code><samp>Private Sub Combo0_KeyDown(KeyCode As Integer, Shift As Integer)<br />
&nbsp; If KeyCode = 8 Or KeyCode = 17 Or KeyCode = 32 Or KeyCode = 46 Or KeyCode &gt; 47 Then<br />
&nbsp;&nbsp;&nbsp; Me.TimerInterval = 300<br />
&nbsp; End If<br />
End Sub</samp></code></p>

<p>This will make the form do something 300 milliseconds later if the pressed key is one we like, i.e. one that changes the contents of the control in a way that should affect the search. 8 is Backspace, 32 is Spacebar, 46 is Del so we need them. 17 is CTRL and can arrive if the user pastes with CTRL+V or cuts with CTRL+X. Most keycodes over 47 are characters.</p>

<p>See the bottom of this post for an extension to make this handle mouse and ribbon-based paste operations.</p>

<p>Why 300 milliseconds? Make it whatever you like, but I found that is a reasonable amount of time to wait before executing the search if a user has clicked into the control and started typing. Any longer and the user feels like they are having to hang around, any faster and it searches before they’ve had time to enter the characters they are confident about. Note that if the user presses more than one key, for example by typing “Rob”, each will set the form’s timer interval, which re-starts the timer and so defers the resulting action until 300 milliseconds after the last key press. So the 300 really defines the length of pause after hitting any one key.</p>

<p>At this point the observant will be wondering why we are using the Key Down event and not the Key Up event. Well I started off by using Key Up event, but I found that it simply does not fire if the user selects some of the entered text and removes it with the Del key; however using the Key Down event does need extra care, because when it fires the contents of the control have <em>not yet changed to reflect the key press</em>.</p>

<p>Now we need some code on the form’s Timer event.</p>

<p><code><samp>Private Sub Form_Timer()<br />
&nbsp;&nbsp;&nbsp; TimerInterval = 0<br />
&nbsp;&nbsp;&nbsp; SearchByName Combo0<font style="background-color: rgb(255, 255, 0);">.Text</font><br />
End Sub</samp></code></p>

<p>In this case we first switch off the timer, then pass the text that is now in the control into a method that will execute the search. Because we are harvesting the search value now, and not in the Key Down event, we get the value after all key presses have completed. Note that we do have to use the .Text property, and not the .Value property (which is the default if we just pass me!Combo0 as a string).</p>

<p>Now we need to process the search. In this simple example we have a private method in the form’s code-behind:</p>

<p><code><samp>Private Sub SearchByName(SearchString As String)<br />
Dim qryDef As QueryDef<br />
&nbsp; If Len(SearchString) &gt;= 2 Then<br />
&nbsp;&nbsp;&nbsp; Set qryDef = CurrentDb.QueryDefs("qryNameSearch")<br />
&nbsp;&nbsp;&nbsp; qryDef.Parameters("NameSearch") = SearchString<br />
&nbsp;&nbsp;&nbsp; Set Combo0.Recordset = qryDef.OpenRecordset<br />
&nbsp;&nbsp;&nbsp; Set qryDef = Nothing<br />
&nbsp;&nbsp;&nbsp; If Combo0.Recordset.RecordCount &gt; 0 Then Combo0.Dropdown<br />
&nbsp; Else<br />
&nbsp;&nbsp;&nbsp; Set Combo0.Recordset = Nothing<br />
&nbsp; End If<br />
End Sub</samp></code></p>

<p>This relies on a saved parameter query, which in the example above might be something like:</p>

<p><code><samp>PARAMETERS NameSearch Text ( 255 );<br />
SELECT Staff.ID, Staff.LastName<br />
FROM Staff<br />
WHERE Staff.LastName Like [NameSearch] &amp; '*';</samp></code></p>

<p>or if you want to find matches when the user enters characters from the middle of the name as well:</p>

<p><code><samp>WHERE Staff.LastName Like '*' &amp; [NameSearch] &amp; '*';</samp></code></p>

<p>And of course the query can be adapted to return more advanced expressions, as in the screenshot above, and executing the search however best suits the data. And the SQL could be inline if you don’t want a saved query. Or it could be a SQL Server stored procedure, or even a called to a web service that is converted to a recordset on return, etc etc.</p>

<p>If we are in a data entry form where the value always exists in the list, that’s it, job done. If the value may not exist (see the example below) we need to give the user a way of entering data. If the control is being used to navigate to a record, then we need to do something when the user selects a value using a handler on the control’s After Update event, such as:</p>

<p><code><samp>Private Sub Combo0_AfterUpdate()</samp></code></p>

<p><code><samp>&nbsp;&nbsp;&nbsp; If Combo0 &gt; "" Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; … do something …<br />
&nbsp;&nbsp;&nbsp; End If</samp></code></p>

<p><code><samp>End Sub</samp></code></p>

<p>Moving on from this simple example, it’s also possible to show the list of values in a separate control that is made visible when a search finds rows. In the example below (from home!) I’ve used a list box, setting it’s record source in similar fashion to the above combo box. I prefer the look and feel of this approach, and it would also allow the user to select more than one row which could be useful in some situations. The example below is a form used for taking entries to fell races. A large proportion of runners have entered before, so it saves a lot of time to fetch and confirm their previous details rather than starting from scratch. On the other hand, lots of people share the same names, particularly in South Wales(!), so we need to see quite a lot of information to confirm a match.<br />
When the user enters two or more characters of the surname, a list appears. On selecting a row, the remaining details are automatically filled in (in the parts of the form hidden by the list in this image). If a match is not found, the user has a simple data entry form to enter a new competitor.</p>

<p><img alt="Incremental search in Access using a list box" border="0" height="412" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_142.png" style="border: 0px currentColor; border-image: none; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Incremental search in Access using a list box" width="640" /></p>

<p>In this case our search method looks like this, to show or hide the list:</p>

<p><code><samp>If Len(RunnerSearch.Text) &gt;= 2 Then<br />
&nbsp; Set qryDef = CurrentDb.QueryDefs("RegistrationFormNameSearchQry")<br />
&nbsp; qryDef.Parameters("NameSearch") = RunnerSearch.Text<br />
&nbsp; Set lstRunnerPicker.Recordset = qryDef.OpenRecordset<br />
&nbsp; Set qryDef = Nothing<br />
&nbsp; lstRunnerPicker.Height = 567 * 4.9 ' 567 twips per cm<br />
&nbsp; lstRunnerPicker_Label.Height 567 * 4.9<br />
&nbsp; lstRunnerPicker.Visible = True<br />
Else<br />
&nbsp; lstRunnerPicker.Visible = False<br />
End If</samp></code></p>

<p>I hope someone finds this helpful. I’m sure these approaches can be refined and improved in all kinds of ways, so feedback welcome.</p>

<h5>UPDATE @ 4th December 2016</h5>

<p>When checking&nbsp;out comments from Rick Wiker below,&nbsp;I noticed my code above doesn't detect right-click &gt; Paste, or Paste using the ribbon tool. I find this is easily solved using the On Change event, eg:</p>

<p><code>Private Sub <font face="Courier New">RunnerSearch</font>_Change()<br />
&nbsp; If Me.TimerInterval = 0 Then<br />
&nbsp;&nbsp;&nbsp; Me.TimerInterval = 300<br />
&nbsp; End If<br />
End Sub</code></p>

<p>The condition is probably not needed -&nbsp;I just used it to be specific about when this was happening (i.e. when the KeyDown event has not already set the timer). This seems to work for a text box search control for both mouse and ribbon-based pasting. I've not yet tested this approach with a combo, and haven't spotted any negative side effects. Feel free to comment!</p>
<br /><a href='https://www.esdm.co.uk/how-to-do-a-typeahead-incremental-search-control-in-microsoft-access'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/how-to-do-a-typeahead-incremental-search-control-in-microsoft-access'>...</a>]]></description>
      <link>https://www.esdm.co.uk/how-to-do-a-typeahead-incremental-search-control-in-microsoft-access</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/how-to-do-a-typeahead-incremental-search-control-in-microsoft-access</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/how-to-do-a-typeahead-incremental-search-control-in-microsoft-access</guid>
      <pubDate>Sun, 11 Jan 2015 16:53:00 GMT</pubDate>
    </item>
    <item>
      <title>Confusion when renaming SQL Server views</title>
      <description><![CDATA[<p>If you rename a view through the interface in SQL server, it can lead to strange behaviour later on if you ever come to recompile the view. Symptoms include views failing to recompile and, in extreme cases, the entire structure of a view (not necessarily the one you're recompiling) changing when the&nbsp;built-in <code>sp_refreshview</code> procedure is run.</p>

<p>Behind the scenes, SQL Server stores information about the views in its system tables;&nbsp; the key one for our purposes here is <code>Information_Schema.Views</code>, which includes, among other things, the view's name and its SQL definition, something like the example below:</p>

<table border="1" cellpadding="1" cellspacing="1" height="93" width="100%">
	<tbody>
		<tr>
			<th>Name</th>
			<th>Definition</th>
		</tr>
		<tr>
			<td>vieContacts</td>
			<td>
			<p>Create View vieContacts as Select ContactID, Name, PhoneNumber from ContactsTable</p>
			</td>
		</tr>
	</tbody>
</table>

<p>Now, suppose I rename <code>vieContacts</code> in the interface to <code>OldVieContacts</code>. This will update the name in the system table, but does not update the definition, so I end up with the following:</p>

<table border="1" cellpadding="1" cellspacing="1" height="112" width="100%">
	<tbody>
		<tr>
			<th>Name</th>
			<th>Definition</th>
		</tr>
		<tr>
			<td>OldVieContacts</td>
			<td>
			<p>Create View vieContacts as Select ContactID, Name, PhoneNumber from ContactsTable</p>
			</td>
		</tr>
	</tbody>
</table>

<p>When I come to recompile the view, it will fail, giving me the error "<code>Msg 208, Level 16, State 6, Procedure sp_refreshsqlmodule_internal, Line 75<br />
Invalid object name 'vieContacts'.</code>" This is because the name doesn't match what is stored in the definition.</p>

<p>Unfortunately, the problem doesn't stop there. If I use the copy/paste funtion in SQL Managent Studio, I can copy a view, rename it so that it's a backup and then edit my original. This sounds like a good idea, but because of the problems that renaming causes, it can lead to major headaches later on. To illustrate why this can be so confusing, let's imagine I copy my vieContacts, rename the copy to OldVieContacts and modify my vieContacts to include another couple of fields. After this, I would have the following in <code>Information_Schema.Views</code>:</p>

<table border="1" cellpadding="1" cellspacing="1" height="112" width="100%">
	<tbody>
		<tr>
			<th>Name</th>
			<th>Definition</th>
		</tr>
		<tr>
			<td>OldVieContacts</td>
			<td>
			<p>Create View vieContacts as Select ContactID, Name, PhoneNumber from ContactsTable</p>
			</td>
		</tr>
		<tr>
			<td>vieContacts</td>
			<td>
			<p>Create View vieContacts as Select ContactID, Name, PhoneNumber, Address, Email from ContactsTable</p>
			</td>
		</tr>
	</tbody>
</table>

<p>What makes this such a potential problem is that if I recompile OldVieContacts (and this might be done as part of a script that refreshes all the views) the inbuilt function reads the definition out of <code>Information_Schema.Views</code>, which tells it to rebuild vieContacts, so the result is that vieContacts gets overwritten and I would see my two new columns disappear from it. Worse, the refresh process also updates the definition in <code>Information_Schema.Views</code>, so now vieContacts would be stuck with the incorrect structure and no easy way to fix it.</p>

<p>So what can you do about these problems? Well, here are some suggestions:</p>

<ol>
	<li>Prevention is better than cure. Don't rename views thorugh the SQL Management Studio interface.</li>
	<li>If you need to change the name of the view, drop the view and recreate it with the new name - you can use the "Script View As" option to create a SQL script that will do the drop and create for you, and you can just change the name in the script for the creation before running it.</li>
	<li>If you're lucky, you may be able to rename the view in the interface back to its old name. This isn't recommended though as it's not as sure as other methods.</li>
	<li>Rather than copying a view, just copy the SQL definition from the designer into a new blank view. This may seem more complex than just copying &amp; pasting the view, but it will be better in the long term</li>
	<li>If you suspect that some of your views may suffer from this issue, you can check to see how severe the problem is by running this SQL command from a query window: <code>Select TABLE_NAME, VIEW_DEFINITION from INFORMATION_SCHEMA.VIEWS where VIEW_DEFINITION not like '%' + TABLE_NAME + '%'</code> - this will identify the views that need attention.</li>
	<li>You can repair a view that has suffered in this way by running an <code>Alter View</code> script to alter the view to the correct definition.</li>
</ol>

<p>The best thing, though, is to avoid renaming views through the SQL Server Management Studio interface.</p>
<br /><a href='https://www.esdm.co.uk/confusion-when-renaming-sql-server-views'>Timothy Derbyshire</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/confusion-when-renaming-sql-server-views'>...</a>]]></description>
      <link>https://www.esdm.co.uk/confusion-when-renaming-sql-server-views</link>
      <author>timothyd@esdm.co.uk (Timothy Derbyshire)</author>
      <comments>https://www.esdm.co.uk/confusion-when-renaming-sql-server-views</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/confusion-when-renaming-sql-server-views</guid>
      <pubDate>Fri, 05 Dec 2014 14:47:00 GMT</pubDate>
    </item>
    <item>
      <title>WMS/WFS down when a GeoServer store is disabled after Windows updates</title>
      <description><![CDATA[<p>Last night some of our servers restarted after scheduled updates, and we awoke to warning emails that some of our GIS web services were down. The affected WMS/WFS services were from <a href="http://geoserver.org/" target="_blank" title="GeoServer web site">GeoServer</a>, so we knew immediately what to look for as this has happened before… the “Store” representing the connection to the GIS data repository – in this case a PostGIS database - had switched itself off, or become disabled:</p>

<p><img alt="GeoServer store disabled after server restart" border="0" height="480" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_139.png" style="border: 0px currentColor; border-image: none; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="GeoServer store disabled after server restart" width="469" /></p>

<p>Ticking the box and saving the Store fixed the problem.</p>

<p>Our hypothesis is that if the GeoServer service is re-started when the database server is not yet available, GeoServer automatically disables the affected Stores. [I later found <a href="http://osgeo-org.1560.x6.nabble.com/PostGIS-Store-disabled-on-server-reboot-td3789469.html" target="_blank">this discussion</a> in which one of the GroServer developers confirms that "the startup code checks if the stores are available, and if not, it<br />
disables them".</p>

<p>We will try to prevent this happening again by ensuring that the GIS server is restarted well after the database servers, but we’d welcome any other ideas.</p>
<br /><a href='https://www.esdm.co.uk/wmswfs-down-when-a-geoserver-store-is-disabled-after-windows-updates'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/wmswfs-down-when-a-geoserver-store-is-disabled-after-windows-updates'>...</a>]]></description>
      <link>https://www.esdm.co.uk/wmswfs-down-when-a-geoserver-store-is-disabled-after-windows-updates</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/wmswfs-down-when-a-geoserver-store-is-disabled-after-windows-updates</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/wmswfs-down-when-a-geoserver-store-is-disabled-after-windows-updates</guid>
      <pubDate>Thu, 13 Nov 2014 09:31:00 GMT</pubDate>
    </item>
    <item>
      <title>MapInfo Pro requests node-locked activation method rather than Licence server details when activating a concurrent licence</title>
      <description><![CDATA[<p>This issue may occur where users have been switched from using node-locked to concurrent MapInfo licences.</p>

<p>Although the served version of MapInfo is installed, when MapInfo is run the dialog for activation asks for the serial number and access code as for a node-locked licence, instead of the licence server name and port number.</p>

<p>This issue is caused by registry keys in HKEY_USERS pointing to the node-locked licence that are conflicting with registry keys in HKEY_LOCAL_MACHINE pointing to the concurrent licence.</p>

<p>To fix this issue:</p>

<ol>
	<li>Ensure that if a node-locked licence was activated it has been transferred back to Pitney Bowes, then close MapInfo.</li>
	<li>Open Registry Editor and go to Edit &gt; Find and search for MINWES.</li>
	<li>Back up the registry using File &gt; Export.</li>
	<li>Remove the folder in HKEY_USERS that contains this key for the affected version e.g. HKEY_USERS\{UserID}\Software\MapInfo\MapInfo\Professional\1250.</li>
	<li>Run MapInfo to check it is now using the concurrent licence.</li>
</ol>

<p>&nbsp;</p>

<p>&nbsp;</p>
<br /><a href='https://www.esdm.co.uk/mapinfo-pro-requests-node-locked-activation-method-for-concurrent-licence'>Claire Lush</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/mapinfo-pro-requests-node-locked-activation-method-for-concurrent-licence'>...</a>]]></description>
      <link>https://www.esdm.co.uk/mapinfo-pro-requests-node-locked-activation-method-for-concurrent-licence</link>
      <author>claire.lush@idoxgroup.com (Claire Lush)</author>
      <comments>https://www.esdm.co.uk/mapinfo-pro-requests-node-locked-activation-method-for-concurrent-licence</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/mapinfo-pro-requests-node-locked-activation-method-for-concurrent-licence</guid>
      <pubDate>Wed, 12 Nov 2014 11:36:00 GMT</pubDate>
    </item>
    <item>
      <title>How to suppress sponsor offers when installing or updating Java</title>
      <description><![CDATA[<p>When you update Java, you are usually offered the Ask toolbar, default home page and search. You have to opt out of this offer every update and, until recently, there was no way to avoid this without a registry hack. However, in summer 2014, Oracle quietly added an option to disable this in Java Configuration. Search from your start menu for Configure Java, go to advanced, and then scroll to the bottom and check ‘Suppress sponsor offers when installing or updating Java’. You will never(?) be tempted by Ask’s wonderful addons again ….</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>
<br /><a href='https://www.esdm.co.uk/how-to-suppress-sponsor-offers-when-installing-or-updating-java'>Andy Brewer</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/how-to-suppress-sponsor-offers-when-installing-or-updating-java'>...</a>]]></description>
      <link>https://www.esdm.co.uk/how-to-suppress-sponsor-offers-when-installing-or-updating-java</link>
      <author>andybrewer@esdm.co.uk (Andy Brewer)</author>
      <comments>https://www.esdm.co.uk/how-to-suppress-sponsor-offers-when-installing-or-updating-java</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/how-to-suppress-sponsor-offers-when-installing-or-updating-java</guid>
      <pubDate>Thu, 23 Oct 2014 07:39:00 GMT</pubDate>
    </item>
    <item>
      <title>SQL Server - Listing all tables in database, the space used and row counts</title>
      <description><![CDATA[<p>This is a very useful SQL script to listing all of the tables in a SQL Server database with the row count and the amount of space being used by that table, handy for troubleshooting databases that are using more space that expected.</p>

<p>CREATE TABLE #RowCountsAndSizes (TableName NVARCHAR(128),rows CHAR(11), &nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp;reserved VARCHAR(18),data VARCHAR(18),index_size VARCHAR(18),&nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp;unused VARCHAR(18))</p>

<p>EXEC &nbsp; &nbsp; &nbsp; sp_MSForEachTable 'INSERT INTO #RowCountsAndSizes EXEC sp_spaceused ''?'' '</p>

<p>SELECT &nbsp; &nbsp; TableName,CONVERT(bigint,rows) AS NumberOfRows,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;CONVERT(bigint,left(reserved,len(reserved)-3)) AS SizeinKB<br />
FROM &nbsp; &nbsp; &nbsp; #RowCountsAndSizes&nbsp;<br />
ORDER BY &nbsp; NumberOfRows DESC,SizeinKB DESC,TableName</p>

<p>DROP TABLE #RowCountsAndSizes</p>

<p>The original details including some other useful scripts can be found using the following link:</p>

<p><a href="http://www.sqlmatters.com/Articles/Listing%20all%20tables%20in%20a%20database%20and%20their%20row%20counts%20and%20sizes.aspx">&nbsp;http://www.sqlmatters.com/Articles/Listing%20all%20tables%20in%20a%20database%20and%20their%20row%20counts%20and%20sizes.aspx</a></p>
<br /><a href='https://www.esdm.co.uk/sql-server-listing-all-tables-in-database-the-space-used-and-row-counts'>Steve Ellwood</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/sql-server-listing-all-tables-in-database-the-space-used-and-row-counts'>...</a>]]></description>
      <link>https://www.esdm.co.uk/sql-server-listing-all-tables-in-database-the-space-used-and-row-counts</link>
      <author>stevee@esdm.co.uk (Steve Ellwood)</author>
      <comments>https://www.esdm.co.uk/sql-server-listing-all-tables-in-database-the-space-used-and-row-counts</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/sql-server-listing-all-tables-in-database-the-space-used-and-row-counts</guid>
      <pubDate>Mon, 22 Sep 2014 15:37:00 GMT</pubDate>
    </item>
    <item>
      <title>RemoteApp connection issue with Server 2012 from Windows 7 &amp; 8 PCs (with Event ID 4625 in the Event log)</title>
      <description><![CDATA[<p>Having just built a nice new shiny Window Server 2012 VM with Remote Desktop Gateway Services installed we encountered a problem where one user was not able to start RemoteApp applications from their home PC&nbsp;even though they were able to launch them from&nbsp;the 2008 R2 server we were using previously. The user was able to access the RDWeb page for the server, but launching any applications failed, as did launching them from RemoteApp RDP files. The error message the user was receiving was as follows (however as seems to be the case with RD Gateway sometimes a different connection failure would be randomly returned):</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/img_29072014_130225.png"><img alt="RemoteApp Disconnected" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/img_29072014_130225_thumb.png" style="border-width: 0px; border-style: solid;" title="RemoteApp Disconnected" /></a></p>

<blockquote>
<p>i.e. Remote Desktop can't connect to the remote computer for one of these reasons:</p>

<p>1) Remote access to the server if not enabled</p>

<p>2) The remote computer is turned off</p>

<p>3) The remote computer is not available on the network</p>

<p>Make sure the remote computer is turned on and connected to the network, and that remote access is enabled.</p>
</blockquote>

<p>After a lot of time trying to work out why this single user could not connect we found an error in the Security Event Log looking similar to that shown below.<br />
NOTE: The Server Manager\Remote Desktop Services\Servers screen in Windows Server 2012 shows Event Log information but it appears that it doesn't show everything relevant,&nbsp;because this error didn’t appear there so we chased our tails for a long time before actually finding this error in the Security Event Log.</p>

<p>------------------------------------------------</p>

<p>Log Name:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Security<br />
Source:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Microsoft-Windows-Security-Auditing<br />
Date:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29/07/2014 15:27:57<br />
Event ID:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4625<br />
Task Category: Logon<br />
Level:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Information<br />
Keywords:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Audit Failure<br />
User:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; N/A<br />
Computer:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myserver.mydomain.local<br />
<br />
Description:<br />
An account failed to log on.</p>

<p>Subject:<br />
&nbsp;&nbsp;&nbsp; Security ID:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NULL SID<br />
&nbsp;&nbsp;&nbsp; Account Name:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -<br />
&nbsp;&nbsp;&nbsp; Account Domain:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -<br />
&nbsp;&nbsp;&nbsp; Logon ID:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x0</p>

<p>Logon Type:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3</p>

<p>Account For Which Logon Failed:<br />
&nbsp;&nbsp;&nbsp; Security ID:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NULL SID<br />
&nbsp;&nbsp;&nbsp; Account Name:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myusername<br />
&nbsp;&nbsp;&nbsp; Account Domain:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mydomain</p>

<p>Failure Information:<br />
&nbsp;&nbsp;&nbsp; Failure Reason:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; An Error occured during Logon.<br />
&nbsp;&nbsp;&nbsp; Status:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0xC000035B<br />
&nbsp;&nbsp;&nbsp; Sub Status:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x0</p>

<p>Process Information:<br />
&nbsp;&nbsp;&nbsp; Caller Process ID:&nbsp;&nbsp;&nbsp; 0x0<br />
&nbsp;&nbsp;&nbsp; Caller Process Name:&nbsp;&nbsp;&nbsp; -</p>

<p>Network Information:<br />
&nbsp;&nbsp;&nbsp; Workstation Name:&nbsp;&nbsp;&nbsp; remotepcname<br />
&nbsp;&nbsp;&nbsp; Source Network Address:&nbsp;&nbsp;&nbsp; -<br />
&nbsp;&nbsp;&nbsp; Source Port:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -</p>

<p>Detailed Authentication Information:<br />
&nbsp;&nbsp;&nbsp; Logon Process:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; Authentication Package:&nbsp;&nbsp;&nbsp; NTLM<br />
&nbsp;&nbsp;&nbsp; Transited Services:&nbsp;&nbsp;&nbsp; -<br />
&nbsp;&nbsp;&nbsp; Package Name (NTLM only):&nbsp;&nbsp;&nbsp; -<br />
&nbsp;&nbsp;&nbsp; Key Length:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0</p>

<p>This event is generated when a logon request fails. The Subject fields indicate the account on the local system which requested the logon. This is most commonly a service such as the Server service, or a local process such as Winlogon.exe or Services.exe.</p>

<p>The Logon Type field indicates the kind of logon that was requested. The most common types are 2 (interactive) and 3 (network).</p>

<p>The Process Information fields indicate which account and process on the system requested the logon.</p>

<p>The Network Information fields indicate where a remote logon request originated. Workstation name is not always available and may be left blank in some cases.</p>

<p>The authentication information fields provide detailed information about this specific logon request.<br />
&nbsp;&nbsp;&nbsp; - Transited services indicate which intermediate services have participated in this logon request.<br />
&nbsp;&nbsp;&nbsp; - Package name indicates which sub-protocol was used among the NTLM protocols.<br />
&nbsp;&nbsp;&nbsp; - Key length indicates the length of the generated session key. This will be 0 if no session key was requested.</p>

<p>------------------------------------------------------------------</p>

<p>After researching this error it turned out that the problem was caused by a local policy setting on the remote PC (not the server). The policy that needed changes was:</p>

<p>Computer Configuration-&gt;Windows Settings-&gt;Security Settings – Network security:LAN Manager authentication level.</p>

<p>The default on Windows 7 &amp; 8 is to not have any value set for this setting, though this particular user's PC it was set to "Send LM and NTLM - use NTLMv2 session security if negotiated&nbsp;"; it needs to be set to “Send NTLMV2 response only”. Once this was done the Windows 8.1 PC was able to open RemoteApp applications without problems.</p>

<p>This setting can be changed using the local group policy editor on the PC (run gpedit.msc) and changing the values as shown below:</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/img_29072014_155257.png"><img alt="Local Policy Group Editor" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/img_29072014_155257_thumb.png" style="border-width: 0px; border-style: solid;" title="Local Policy Group Editor" /></a></p>
<br /><a href='https://www.esdm.co.uk/remoteapp-connection-issue-with-server-2012-from-windows-7-8-pcs-with-event-id-4625-in-the-event-log'>Steve Ellwood</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/remoteapp-connection-issue-with-server-2012-from-windows-7-8-pcs-with-event-id-4625-in-the-event-log'>...</a>]]></description>
      <link>https://www.esdm.co.uk/remoteapp-connection-issue-with-server-2012-from-windows-7-8-pcs-with-event-id-4625-in-the-event-log</link>
      <author>stevee@esdm.co.uk (Steve Ellwood)</author>
      <comments>https://www.esdm.co.uk/remoteapp-connection-issue-with-server-2012-from-windows-7-8-pcs-with-event-id-4625-in-the-event-log</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/remoteapp-connection-issue-with-server-2012-from-windows-7-8-pcs-with-event-id-4625-in-the-event-log</guid>
      <pubDate>Tue, 29 Jul 2014 17:49:00 GMT</pubDate>
    </item>
  </channel>
</rss>