Upgrading SQL’08 to SQL’12 for better Geography support

Users of SQL Server 2008′s native Geography type were a little disappointed when try tried to use geometries such as this one… MULTIPOINT (55 55, -55 55, -55 -55).  SQL Server 2008 responds with the error:

A .NET Framework error occurred during execution of user-defined routine or aggregate “geography”: 

Microsoft.SqlServer.Types.GLArgumentException: 24205: The specified input does not represent a valid geography instance because it exceeds a single hemisphere. Each geography instance must fit inside a single hemisphere. A common reason for this error is that a polygon has the wrong ring orientation. To create a larger than hemisphere geography instance, upgrade the version of SQL Server and change the database compatibility level to at least 110.

If those directions are cryptic, the solution is pretty easy.  

First, back up all your databases, in case something goes wrong.  Second, upgrade your SQL 2008 instance to SQL 2012 (you may have to update your service packs on 2008 first).  Finally, for each database, go into SQL Server Management Studio, and right-click your database to get its properties.  Under Options, change the Compatilibity level to “SQL Server 2012″.

Image

That should be enough to fix these Geography errors.  You can test your success with a simple query such as SELECT geography::STGeomFromText(‘MULTIPOINT (55 55, -55 55, -55 -55)’, 4326)

Disk / File Management in .NET is Lamentable

Its surprising how poor nearly everything in .NET is that has to do with file management.  Here’s two quick examples:

Directory.GetFiles() / EnumerateFiles() / GetDirectories() / EnumerateDirectories()

The methods are the fast replacement methods for the oh-so-slow recursive search technique.  The problem?  If they encounter a folder they don’t have permission for they throw a UnauthorizedAccessException during MoveNext().  In other words, the first folder it can’t access, you cannot continue.  At all.

File.Copy()

This one’s easy.  You cannot cancel File.Copy(), nor get any progress reporting.  If you want those things, you have to do a stream copy or pInvoke the Win32 API.

Don’t create ArcGIS Server 10.1 folder names with Plus Signs (+)

This is funny.  Don’t create ArcGIS Server 10.1 folder names with plus signs “+”.  It can’t easily be deleted, and many attempts to rename it just make weird duplicates.  Somehow I got it to delete by using a period where the plus should be, or something weird and similar.  You can also browse to the config-store folder for you ArcGIS Server and delete it manually.

I’ve already alerted ESRI.

ArcDesktop 10.1 supports GDAL WMS-driver XML Files

New in ArcDesktop 10.1 appears to be support for GDAL WMS XML files.  If you’re not familiar with the concept, you can check out the documentation here.  In 10.0, ArcMap interpreted these GDAL XML files as data files, instead of a raster data source.  If you’re still on 10.0, you can hack around the lack of by creating a VRT that references your XML file.  

Take note, however, of ArcMap’s GDAL version.  ArcMap 10.0 relies on GDAL 1.6, while ArcMap 10.1 uses GDAL 1.8.  Many of the WMS minidrivers don’t appear in earlier GDAL versions.

A couple of final notes about GDAL’s WMS support.  First off, GDAL is built on top of cURL.  One weird fact:  cURL can load file-system and FTP URLs just as well as HTTP ones.  Finally, for all you windows folk, cURL doesn’t automatically pick up on your HTTP proxy settings.  If you want to use Fiddler to see GDAL fetching web content, you’ll have to set an environment variable: http_proxy=http://localhost:8888/.

Get a GUID for anything

It’s a pretty normal task to track unique items using GUIDs.  But what if you don’t have a GUID? Of course you can assign a random GUID, but how do you convert strings to GUIDs using a repeatable process?  The answer is quite simple:  MD5 hashes are 128 bits – the same length as GUIDs.  Here’s how it works…

Using Sql Server:

SELECT CAST(HASHBYTES(‘MD5′, ‘test’) as uniqueidentifier)

Using C#:

MD5 md5 = MD5.Create();
byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(“test”));
Guid guid = new Guid(hashBytes);

 

Both methods will return the same GUID for the string ‘test’:  CD6B8F09-2146-73D3-CADE-4E832627B4F6.

Fast Directory / Folder Copying in .NET

When charged with copying files, my first thought is XCopy or Robocopy.  If the idea of copying files with .NET code seems silly to you, you’re not alone.  File.Copy() lacks cancellation and progress status.  Simply calling Process.Start(“robocopy…”) would have similar challenges.  To make a long story short, I put together some simple code that could easily support progress and cancellation and usually outperforms Robocopy


static void CopyAll(string sourcePath, string destinationPath)
{
	//Now Create all of the directories
	foreach (string sourceDirectory in Directory.GetDirectories(sourcePath, "*.*", SearchOption.AllDirectories))
		Directory.CreateDirectory(sourceDirectory.Replace(sourcePath, destinationPath));

	//Copy all the files
	foreach (string sourceFile in Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories))
		CopyFile(sourceFile, sourceFile.Replace(sourcePath, destinationPath));
}

const int CopyBufferSize = 64 * 1024;
static void CopyFile(string src, string dest)
{
	using (var outputFile = File.OpenWrite(dest))
	{
		using (var inputFile = File.OpenRead(src))
		{
			outputFile.SetLength(inputFile.Length);
			byte[] buffer = new byte[CopyBufferSize];
			int bytesRead;
			while ((bytesRead = inputFile.Read(buffer, 0, CopyBufferSize)) != 0)
			{
				outputFile.Write(buffer, 0, bytesRead);
			}
		}
	}
}