Jul 12

So you want to add your own data? – DataProviders

There are off course several ways to add your own data to the map. You could write your own dialogues, data handlers etc. and add the resulting data to the map as a featureset or raster. In MW4 one needed to do it like that. MW6 offers a neater solution: It lets you integrate your very own data provider for as exotic data formats as you like to a list of available data providers and these appear as valid data sources in the standard open file dialogue.

The principle way to do this is to add your provider, which must implement the IDataProvider interface, to the list of data providers. This is done by registering it with the data manager that handles all data addition. Using this mechanism you can concentrate on the implementation of your data provider, rather than waste time on the UI elements. Data is also correctly added to the legend and symbolised accordingly.

There is a tutorial on how to add your own RasterDataProvider to MW6 (http://www.mapwindow.org/wiki/index.php/MapWindow6:Examples:IntRasterDataProvider). To complement this tutorial, I shall extend on how to develop a sample XYZ vector data provider, which shall take any file format similar to
X,Y,Z
630014.833,4905371.152,47726.031000000
630014.608,4905371.224,47725.793000000
...

and add it to the map as a Point feature layer.

The shown format is quite arbitrary. In my own implementation I have added a dialogue, where you can specify a number of properties of the file format, such as the field separator, the order of the fields, whether the file has a header line, and whether it contains line numbers or ID fields. Often this format is called CSV; I prefer to call it XYZ, to emphasise the spatial nature of the file, and also, because the comma (as in Comma Separated Values, hence CSV) is by no means common. Often a semicolon is used instead, especially, if data originates in Europe, where the comma often serves a decimal separator. I leave it to your own creativity to accomodate any format that you wish.

I have not yet found the correct way of dealing with unrecognised data, i.e. returning a null-dataset. This will crash the application at the moment. Also, at the time of writing this, I have not found a way to add more than one file at a time, making it a very boring task of adding several to many files of the same kind.

(to be continued)

Jul 12

I am rather a fan of GIS (Geographic Information Systems). I started off many years ago with ESRI’s ArcView® 3.x, then progressing to ArcGIS® 8.x / 9.x. Almost all of my projects I based on the extensions idea that makes ESRI’s GIS products so powerful. There are a number of applications for the E.O.D. (Explosives Ordnance Disposal) that I started off, such as the various modules of KMRPAS, a QS/Controlling application that can be used in the field for documentation of the ongoing work, and in the office for the bill of quantities and generating maps for the client. As the name implies, the prototype of KMRPAS had been coded with a PASCAL derivate: Delphi 5. The project was later converted to VB.NET, when the further development was commercialised.

Since then I had not been able to afford to keep up with the yearly updates necessary to stay in tune with ESRI’s new versions. If you are free-lance you just cannot pro-actively invest 2000 or so Euros in a software that might or might nor generate revenue.

So I was looking around for alternatives: I tried SharpMap, but I had the feeling that development somehow stalled a little when the originator of the project himself joined ESRI’s team. Also, SharpMap was (or is) more a framework that you use building your own application from scratch. What I did, though, use some of the extensive sources to gain more insight into spatial implementations.

Some time ago I purchased a book that elaborated on the use of open source products for desktop GIS (forgot the exact name, though). Amongst all the candidates from the pre-dominatly Linux world (QGIS, Grass) and their (much hated by me) C/C++ API’s, one mentioning caught my interest: MapWindow 4. This is a product that can be used in a variety of different scenarios: As a stand-alone desktop GIS, giving you a broad set of functions, both on raster and vector data, and, which can be extended by plug-ins, a lot of which are freely available on the web. Secondly, you can use MapWindow 4 as the basis for your own extensions, pretty much like the good old ArcGIS® stuff I wrote some years ago. By implementing a plugin interface by any language that is able to utilise ActiveX, you write your own application on top of MapWindow 4, and through some internal magic the plugin appears in the list of available plugins and can there be activated. Thirdly, you can use any COM-aware language to write your own application from scratch, embedding MapWindow 4 functionality by using the ActiveX control. This is a little more (or even more) work that the plugin/extension approach, but you end up with an application that looks truly yours.

Since MapWindow 4 is open source, it would also be possible to extend the source code itself and recompile your very own version of MapWindow 4. However, I consider this bad practise, as you potentially en up with a myriad of concurrent versions of the same product. Plus, Version 4 is based on C++ sources, counting me out for source code manipulation, anyway ;-) .

With the advent of Version 6 of MapWindow the developer team has migrated to .NET as their development environment, utilising the C# language. Development is volatile and things get broken, re-assembled, re-broken etc., often. But I decided to base all my future development on this version. I my solution for my magnetic utilities I have added MapWindow 6 straight from the project’s SubVersion directory, keeping it current from within the IDE (I use SharpDevelop 3.1 for this now). There are a couple of tutorials on the web, however, they are a little hard to find and cover only a tiny proportion of what is already possible using MapWindow 6. There is also very little documentation as yet; understandable, though, as the team is obviously wanting to distribute a halfway stable version first, before indulging into the boring old word processing.

To build a first runnable application using the MapWindow 6 framework, create a new Windows forms application from within your favourite .NET IDE. As said before, I use SharpDevelop 3.1, mainly for the ability to make use of solution folders, which I extensively use. However, Visual Studio 2008 in its Express edition is also available at not cost, but doesn’t support solution folders (a minor flaw, I’d say…).

If you want to keep it simple, add the MapWindow6 assembly as a reference (MapWindow.dll in the /Releases/ folder). Also, import the available toolbox items, in order to build your application with drag and drop components. Again, to do that add the MapWindow.dll assembly to your tool box manager (Using SharpDevelop the procedure is a little different from VS2008). You will find that by the time of writing this 57(!) toolbox items are added. A lot of these seem to be items to aid the core development of MapWindow 6, but a two handfuls of components can well be used in your own application (off course, so can the others, but I suspect that they will eventually disappear from the publicly available components. So, I’d rather stay away from them).

At this point a word of criticism: Why on earth some of the Components have small letters at the beginning of their name remains completely non-obvious to me. They are not private, nor internal, but published on the toolbox. There are a number of other code formatting and naming issues, but then, maybe I am prejudice from my experience with ESRI-stuff, which has deeply influenced my own coding style (I still call classes that implement an interface XxxxClass / IXxxx. If they don’t, I don’t call them …Class. I also count up my interfaces, if I decide to extend my implementations. Examples: IPoint / IPointClass / IPoint2).

Let’s move on to creating our first MapWindow6 application. These are the steps:

1. Create a new Windows forms application and give it whatever name.
2. Add a menu strip to it and call it menuMain or whatever you like. You could implement a number of menu items and attached some event handling to it, such like providing and Exit function to leave the application.
3. Add a statusStrip. You could use the one provided by .NET, but the MapWindow6 team has implemented its own version of a statusStrip, called mwStatusStrip (see above!). This neatly fits into the other MapWindow components by providing a couple of automatic features for displaying Map and Legend statii.
4. Add a .NET ToolStripContainer to your form and make it dock in filled mode. You don’t actually need this, if you are not planning to be able to dock your toolbars in other locations than the design one.
5. Add the mwToolStrip component to your toolstrip container. This is a preconfigured tool strip adding basic functionality to your map without any implementation efforts on your side.
6. Now add a SplitContainer filling the rest of your form’s area automatically. Again, this is not a requirement, but if you like the MapWindow4 application’s layout, this is achieved best using the SplitContainer.
7. In the SplitContainer’s left area add a Legend from the MapWindow components. Make it dock in filled mode.
8. Do the same on the right, but this time adding a Map component.
9. Make sure that you go through the properties of each MapWindow6 component that you added to your form (i.e. mwStatusStrip, mwToolStrip, Legend and Map), and ensure that the mutual references are properly set. For example: the map component has a property called Legend. This should be set to your Legend component. The toolstrip will certainly give you an “object null reference” exception, if you do not correctly set its Map property. I fell over that to begin with and was looking for the reason for hours.
10. Build and run your Form. It should do this without errors and you get a basic MapWindow6 application to which you can add raster (bitmaps) and shapefiles already. Also, you can zoom, pan and select, without having written a line of code yourself.

I hope you had success following these suggestions and I have awoken your interest for GIS in general and MapWindow6 in particular. Once I mastered the mechanism to upload screen shots to my site, I shall certainly include some of those in the future.

Until then,

Stay out of mischief!.

Herman the German

Jul 12

I have spent a few days on the framework and the associated documentation, which one can purchase through various science articles websites. There is a document included with the framework sources, written by one of the authors of the software, however, with the little documentation that there is in the sources, I wanted to collect as much information I could possibly get.

To follow whatever might be worth following on my site, go ahead and donwload the framework form the GEOMETRICS ftp server (ftp://geom.geometrics.com/pub/mag/Software/magtoolkit-1.0.zip) and unzip it into a folder of your choice.

When I’d done that I very quickly started to convert a number of classes to C#, with which I am much more comfortable that the C/C++/Fortran that makes up the file content of the framework. As I progressed I realised that I would spent a heck of a lot of time just trying to convert the code, without properly knowing the workings behind it. So I tried another approach:

I re-installed my Visual Studio 2005, this time including the previously ignored option of a C++ development environment. Having done so I was able to load the C++ solution files that are part of the open source framework. Since I happended to ignore C/C++ during my life so far, I am not familiar with the products that might have created the files of the framework, but my guess is that it must have been something like Visual C++ by Microsoft, as the solution files “*.dsw” opened without problem in VS2005. You’ll find the solution file (maginvlib.dsw) very intuitively(kidding, off course. There is nothing intuitive about MagToolKit, be it naming strategy, grouping files and classes that belong together etc.) in the “maginvlib”-folder. It very well opens and you should have the project layout appearing in the Solution Explorer.

[screenshot_vs]

There might be an error message (I experienced that issue) that the project “maggrad” could not be opened. However, the project should be there in your source code tree, too. You should add it manually, just like you should add the “bdipole” and “Dipole” projects to the solution. The is also a project called TestConsole that might be worth adding (As I stated above, at this point I am pretty clueless as in what is needed, what not, and what I can do with the stuff).

A first attempt to compile the “maginvlib” and “bdipole” projects give you – success!!! I was pretty surprised about that, after all, we were compiling into a .NET framework from a seemingly native C/C++ source. I suppose that is one of the advantages using C/C++, that is pretty much compiles anywhere, as long as you stick to some standard implementation of the language (which one might that be? ;-) ).

Still in the absence of any deeper insight, from the usability point of view, I think I do away with wanting to recode everything in C# (unless someody else would be interested to join into that effort), but rather use the compiled library and wrap around a nice and compact .NET API.

Leaving at this first result, I shall now find out what I can do with the library. But that is for other posts to come. Until then, Cheerio.

Jul 12

My magnetic utilities demand the use of more than a single database. Browsing the web for solutions utilising NHibernate I came across a number of different ideas. However, I found these solutions were commonly geared towards using them with ASP.NET, and not Windows.Forms. Forms allows the implementation of such a framework to be somewhat simplified with regards to caching object states. Here is my solution:

#region System assemblies
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

using System.IO;
using System.Data;
using System.Reflection;
using System.Configuration;
#endregion

#region 3rd-Party assemblies
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Cache;

using log4net;
using log4net.Config;

using System.Data.SQLite;
using Npgsql;
using NpgsqlTypes;
#endregion

namespace MagMA.Data.Access.Sessions
{
    /// <summary>
    /// Singleton implementation of a NHibernate session manager.
    /// </summary>
    public sealed class SessionManager
    {
        #region Singleton pattern
        // lazy initialisation, therefore initialised to null
        private static SessionManager instance = null;

        public static SessionManager Instance
        {
            get { return GetInstance(); }
        }

        public static SessionManager GetInstance()
        {
            // lazy init.
            if (instance == null)
                instance = new SessionManager();

            return instance;
        } // GetInstance
        #endregion

        #region Private fields
        private string configPath;

        // local "caching" of session factories
        private static IDictionary<string,NHibernate.ISessionFactory> sessionFactories = null;
        private static IDictionary<string, NHibernate.ITransaction> transactions = null;
        private static IDictionary<string, ISession>  sessions = null;
        private static System.Configuration.Configuration config = null;
        #endregion

        #region Constructor(s) / finaliser
        /// <summary>
        /// Initializes a new private instance of the <see cref="SessionManager"/>
        /// class to enfoce the singleton pattern.
        /// </summary>
        private SessionManager()
        {
            configPath = Assembly.GetEntryAssembly().Location;
            config = System.Configuration.ConfigurationManager.OpenExeConfiguration(configPath);

            sessionFactories = new Dictionary<string, NHibernate.ISessionFactory>();
            transactions = new Dictionary<string, ITransaction>();
            sessions = new Dictionary<string, ISession>();
        } // constructor

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="SessionManager"/> is reclaimed by garbage collection.
        /// </summary>
        ~SessionManager()
        {
            if (sessionFactories != null)
            {
                CloseAllSessions();
                sessions = null;

                foreach (ITransaction transaction in transactions.Values)
                {
                    transaction.Dispose();
                } // foreach
                transactions.Clear();
                transactions = null;

                foreach (ISessionFactory factory in sessionFactories.Values)
                {
                    factory.Close();
                } // foreach

                sessionFactories.Clear();
                sessionFactories = null;
            } // if
        } // finaliser
        #endregion

        #region Private methods
        private ISessionFactory getSessionFactory(string factoryName)
        {
            try
            {
                if (String.IsNullOrEmpty(factoryName))
                    throw new ArgumentNullException("sessionFactoryName may not be null nor empty");

                if (sessionFactories == null || !sessionFactories.ContainsKey(factoryName))
                {
                    object entry = config.GetSection("nhibernateSettings");
                    SessionFactoriesSection section = (SessionFactoriesSection)entry;
                    if (section == null)
                        throw new NHibernate.SessionException("Could not retrieve a SessionFactoriesSection object.");

                    SessionFactoriesCollection coll = section.SessionFactories;
                    if (coll == null)
                        throw new NHibernate.SessionException("Could not retrieve a SessionFactoriesCollectionn object.");

                    SessionFactoryElement elem = coll[factoryName];
                    if (elem == null)
                        throw new NHibernate.SessionException("Could not retrieve a SessionFactoriyElement object.");

                    string sessionFactoryPath = elem.FactoryConfigPath;
                    NHibernate.Cfg.Configuration cfg = new NHibernate.Cfg.Configuration();
                    cfg.Configure(sessionFactoryPath);
                    ISessionFactory factory = cfg.BuildSessionFactory();

                    sessionFactories.Add(factoryName, factory);
                } // if

                return sessionFactories[factoryName];
            } // try
            catch (Exception ex)
            {
                return null;
            } // catch
        } // getSessionFactory
        #endregion

        #region Public methods
        public ISession GetSession(string factoryName)
        {
            ISession session = null;
            if (sessions.ContainsKey(factoryName))
            {
                session = sessions[factoryName];
            } // if

            if (session == null)
            {
                session = getSessionFactory(factoryName).OpenSession();
            } // if

            if (session == null)
                throw new ApplicationException("Could not get or create session.");

            return session;
        } // GetSession

        public void CloseSession(string factoryName)
        {
            ISession session = null;
            if (sessions.ContainsKey(factoryName))
            {
                session = sessions[factoryName];
            } // if

            if (session != null && session.IsOpen)
            {
                session.Close();
            }
        } // CloseSession

        public void CloseAllSessions()
        {
            foreach (ISession session in sessions.Values)
            {
                session.Close();
            } // foreach
            sessions.Clear();
        } // CloseAllSessions

        public void BeginTransaction(string factoryName)
        {
            ITransaction transaction = transactions[factoryName];

            try
            {
                if (transaction == null)
                {
                    transaction = GetSession(factoryName).BeginTransaction();
                    transactions.Add(factoryName, transaction);
                } // if
            } // try
            catch (Exception ex)
            {
                // TODO: add logging
            } // catch
        } // BeginTransaction

        public void CommitTransaction(string factoryName)
        {
            ITransaction transaction = transactions[factoryName];

            try
            {
                if (transaction != null && !transaction.WasCommitted
                                        && !transaction.WasRolledBack)
                {
                    transaction.Commit();
                    transactions.Remove(factoryName);
                } // if
            }// try
            catch (HibernateException)
            {
                RollbackTransaction(factoryName);
                throw;
            } // catch
        } // CommitTransaction

        public void RollbackTransaction(string factoryName)
        {
            ITransaction transaction = transactions[factoryName];

            try
            {
                transactions.Remove(factoryName);

                if (transaction != null && !transaction.WasCommitted
                                        && !transaction.WasRolledBack)
                {
                    transaction.Rollback();
                } // if
            } // try
            finally
            {
                CloseSession(factoryName);
            } // finally
        } // RollbackTransaction
        #endregion
    } // class
} // end namespace
Jun 21

I have recently discovered the magnetic-inversion-library that has been released into the openSource world by one of the leaders of Magnetic Field Measurement systems: Geometrics. Whilst at first, I was excited, as I have, for a while, been putting together a number of tools to manage, analyse and visualise magnetic field measurement data, such as is collected during E.O.D. operations and de-mining. Along with the source code library, which is in C, C++ and some “old” Fortran, comes a text file, roughly describing what the framwork is, what it can do etc. Also, on the web, I purchased a scientific article on the framework. This papers are very mathematical, however, they do not provide very practical information on how to use the framework in real life.

Hoping, that any more detailed information on the subject shall benefit a broader audience, I shall report my progress (if there is any ;-) ) using this media.

Fell free to contact me if you have any information that might help to shed some light onto the subject.

preload preload preload