UPDATE: Status of DotSpatial on OS X and Linux

Jul 20, 2014 at 8:51 PM
I’ve finally gotten the Winforms-based DemoMap example program to run with Mono on OS X and Linux. Over the years I had never gotten much beyond an immediate crash at startup. Probably improvements both to DotSpatial trunk (particularly the revamped plugins that DemoMap uses) and to the Mono runtime account for this.

There are still several things that have to be patched in DotSpatial before you can compile and run DemoMap, but they’re easy to make. See the list below. Note that these patches only apply to DotSpatial trunk source.

Note you’ll need an up-to-date Mono. I tested with 3.4 on OS X and built 3.6.1 from Mono trunk on Ubuntu Linux 12.04. A ready-to-install Mono package for OS X is available from mono-project.com. If you have the latest Ubuntu 14.04 it looks as though a package is available here:
http://www.ubuntuupdates.org/package/core/trusty/main/base/ubuntu-mono

Be aware that Winforms programs generally look pretty ugly on OS X and Linux. This is especially true of DemoMap, which uses a lot of Windows-specific controls. These controls in many cases operate okay, but they look and feel decidedly out of place on non-Windows. Whether this makes a DotSpatial program unacceptable is a judgement call. Certainly this would probably horrify many OS X and Linux users, but for a specialized GIS solution it might do in a pinch (where the alternative is nothing).

OS X programs share a common menu bar at the top of the screen. Ubuntu’s Unity environment uses something similar now too. So on both platforms the teeny tiny menu attached to the main form looks pretty weird. But the point of Mono’s Winforms support is that it emulates .NET’s Winforms as closely as possible, for better or worse. The common dialogs are bizarre too. For example, the Open dialog is old-style Windows through and through, even down to “My Computer” and “Personal” tabs, neither of which makes sense on OS X or Linux.

Note that DemoMap on Linux is more stable than on OS X and shape labels don’t display at all on OS X for some reason. There are also some things that apparently don’t work correctly in the plugins. I don’t have Windows here to be sure whether what I’m seeing is in the plugins or due to Mono differences.

If there’s interest I could probably upload DemoMap .dmg and .deb packages for OS X and Ubuntu/Debian if anyone wants to see what a single-file installer for a DotSpatial program might look like on those platforms and whether they're worth pursuing. All you would need would be a Mac with up-to-date Mono or Ubuntu with up-to-date Mono. If you don’t have Ubuntu you can install and run it in Oracle’s free VirtualBox (that’s how I’m running Linux on OS X).


The patches:

(1) DemoMap.csproj

Change Platform from “x86” to “AnyCPU” or xbuild won’t compile the project.


(2) DotSpatial.Plugins.ShapeEditor.csproj

Change the OutputPath in two places from “Windows Extensions” to “Plugins” or apps running on Mono will not see this plugin.


(3) DotSpatial.Plugins.ShapeEditor/Properties/AssemblyInfo.cs

Take out the NeutralResourcesLanguage line or any program that uses this plugin will crash. This line doesn’t appear in other plugins so it’s probably just an oversight.


(4) DotSpatial.Plugins.ExtensionManager/ExtensionManagerForm.cs

This plugin calls the Windows uxtheme.dll system library, meaning it will crash on non-Windows. The declaration of the uxtheme external function is okay as long as you don’t call the function except on Windows. So change the SetTheme method so it looks like this:
        if (Environment.OSVersion.Platform == PlatformID.Win32NT)
            SetWindowTheme(((ListView)sender).Handle, "Explorer", null);
A better solution might be just to eliminate the use of uxtheme.dll altogether since it doesn’t appear to be used elsewhere in DotSpatial. Maybe its use is just an oversight — a comment in the code on why it’s used would have been helpful.


(5) DotSpatial.Symbology.Forms/FontFamilyControl.cs

There is no Arial font on Linux, so attempting to use Label Setup will crash the program on Linux. Add two lines below the Arial line in the FontFamilyControl constructor:
        ffdNames.SelectedItem = "Arial";
        if (ffdNames.SelectedItem == null)  //Arial does not exist on Linux
            ffdNames.SelectedItem = FontFamily.GenericSansSerif.Name;
Use of Arial elsewhere in DotSpatial appears to be safe (although perhaps not optimal) since creating a font like this appears to follow the .NET convention of substituting a default font if the specified font is not available:
new Font("Arial", 20, FontStyle.Bold)

(6) DotSpatial.Controls/DefaultMenuBars.cs

If no printer is installed, attempting to use Print Layout will crash the program (probably true on Windows too). PrintLayout_Click’s code should look like this for now:
        if (new System.Drawing.Printing.PrinterSettings().IsValid)
        {
            using (var layout = new LayoutForm())
            {
                layout.MapControl = App.Map as Map;
                layout.ShowDialog();
            }
        }
        else
        {
            MessageBox.Show("No printers installed.");
        }

(7) DotSpatial.Controls/LayoutForm.cs

Four of the ToolStripContainer’s five ToolStripPanels have a 0 Width or Height. This results in a crash deep within Mono’s System.Windows.Form code. Insert this code after the InitializeComponent call in the LayoutForm constructor as a workaround for now:
        if (DotSpatial.Mono.Mono.IsRunningOnMono())
        {
             //On Mac and possibly other Mono platforms, GdipCreateLineBrushFromRect
             // in gdiplus native lib returns InvalidParameter in Mono file LinearGradientBrush.cs
             // if a StripPanel's Width or Height is 0, so force them to non-0.
            _toolStripContainer1.TopToolStripPanel.Size = new System.Drawing.Size(_toolStripContainer1.TopToolStripPanel.Size.Width, 1);
            _toolStripContainer1.BottomToolStripPanel.Size = new System.Drawing.Size(_toolStripContainer1.BottomToolStripPanel.Size.Width, 1);
            _toolStripContainer1.LeftToolStripPanel.Size = new System.Drawing.Size(1, _toolStripContainer1.LeftToolStripPanel.Size.Height);
            _toolStripContainer1.RightToolStripPanel.Size = new System.Drawing.Size(1, _toolStripContainer1.RightToolStripPanel.Size.Height);
        }

(8) Compiling and running

In a terminal, change to the DemoMap folder in the DotSpatial source checkout and enter:

xbuild /property:TargetFrameworkProfile= /property:SolutionDir="../"

This should compile both DemoMap and DotSpatial assemblies into the checkout’s bin/Debug folder.

Repeat this in the folder of each plugin that you want to use.

To run DemoMap, change to bin/Debug and enter:

mono DemoMap.exe
Developer
Jul 21, 2014 at 7:13 AM
Edited Jul 21, 2014 at 7:13 AM
Hi, thank you for this great research!

Regarding to patches
1) DemoMap already AnyCPU, it was x86 in older versions (a half year ago).
2) I've moved that plugin into Plugins folder. Please look at https://dotspatial.codeplex.com/workitem/25044 If you have free time, can you order all plugins?
3) Yes, there is no sense in that line. I've removed it.
4) ExtensionManager uses a lot of Windows specific features. I've moved it into "Windows Extensions" folder.
5) Fixed
6) In Windows this not crashes the program. Windows shows the message that no printers installed. Fixed for Mono.
7) Applied your changes.
Jul 25, 2014 at 10:01 PM
I have tested this on OSX and everything seems to be working great except the SpatialLite plugin. Thanks!

I have been working on porting DotSpatial to Mac( and possibly to Linux too). Xamarin who recently took over Mono have been doing lots of great work and have made MonoMac work very well. MonoMac allows us to use Cocoa libraries for the GUI stuff instead of WinForms. I have been using MonoMac and after changing a few things around in a local copy of DotSpatial, I have been able to get the map working with Cocoa:
Image

Mono also made a great tool for programming up your UI once and being able to set weather you are using WPF(windows), GTK(Linux), or Cocoa(OSX).
https://github.com/mono/xwt

If we could replace as much of the windows forms as possible with Xwt then DotSpatial would be much more cross-platform and look much more native.

What do you both think?
Jul 27, 2014 at 11:57 PM
Your map looks very nice. Maybe you could write up something describing what you modified and what your approach has been.

Xwt and MonoMac assemblies are both included with Xamarin Studio. I don't know how (or if) Xwt is used in Xamarin Studio since it does not include any templates for creating an Xwt solution.

If anyone is interested in testing Xwt, copy and paste the sample code give in the Hello World app (https://github.com/mono/xwt), changing "ToolkitType.Gtk" to "ToolkitType.Cocoa".

Copy MonoMac.dll, Xwt.dll and Xwt.Mac.dll to the same folder as your test code (eg, test1.cs).

Compile: mcs -r:Xwt test1.cs
Create app bundle: macpack test1.exe
Copy dll's into bundle: cp -p *.dll test1.app/Contents/Resources

Now double click the .app in Finder to run it. It won't have a menu, but at least you can see that Xwt works.


I've worked with cross-platform UI toolkits before. They always look great at first and the ability to easily target multiple platforms with the same codebase, yet adapt to each platform's UI conventions, can seem pretty exciting. At first, that is. Then you realize they're incomplete, or that some of the cross-platform widgets don't behave the same across all platforms, or that the toolkit's development is very slow - and suddenly one day you realize you're no longer solving your own app's problems, but spending most of your time solving the UI toolkit's problems. Then everything becomes very depressing and very sad all at once.

Xwt appears to be exactly that kind of toolkit: incomplete, maybe untested, practically unsupported by either developers or users (take a look at how few forum posts or commits in the last 6 months).


Another approach would be not to try to salvage the DotSpatial WinForms UI stuff, but to create a new set of "DotSpatial" controls based on MonoMac. Then you don't have to worry about maintaining compatibility with other platforms and the WinForms users don't have to worry about sudden instability introduced in what they're using.

DotSpatial apps have a very conventional look and feel to them that would have looked perfectly fine 10 years ago. The menus, the toolbar, the gigantic dialog boxes that have so much stuff in them that they need tabbed inputs, etc. Junking most of that on the Mac side would provide a way of starting with fresh ideas. For example, look at how different this new Mac-only IDE looks compared to conventional IDE's: http://www.remobjects.com/elements/fire/

Note that my thinking is that the few Linux desktop users would probably be okay with a WinForms UI.

Thanks.

-Phil
Aug 6, 2014 at 2:35 AM
OK so I made a branch called MacCompatibility for working on this. After making the branch I committed everything I have done so far.

See: changeset 74813

Basically I just made a version of Map that is based on a Cocoa NSView instead of a Windows Forms UserControl. I placed it in a new assembly called DotSpatial.MacControls so that you can include it only if it is needed. I also made a simple demo map that doesn't use any plugins (yet) which uses the map and gives you some functionality so that you can test it.

You should be able to run DemoMap which will use Windows Forms, or DemoMapMac which uses Cocoa.
Developer
Aug 6, 2014 at 3:31 PM
Jacob,

i can't open DemoMapMac project in Visual Studio 2013. It says "The application which this project type is based on was not found."
Regarding to your changes:
  1. Lets rename DotSpatial.MacControls to DotSpatial.Controls.MonoMac or DotSpatial.Controls.Mono (if they will work in Linux too).
  2. Why you need MapCore class in DotSpatial.Controls? As i understood, we can't use inheritance between WinForms Map and MonoMac Map.
  3. Why you need another MapCore inside MacControls? Are you planning to add another inheritors? I think we not need MapCore at this stage.
  4. Try to not use "Mac" abbreviation for your classes and properties, e.g. MacMap, MacCursor, etc. "Mac" inside namespace name will be enough.
  5. We should use code sharing as much as possible to avoid a lot of copy-pastng between Windows and Mac assemblies. If you can't use inheritance try to use aggegation.
Aug 6, 2014 at 3:59 PM
Yes you shouldn't be able to open the DemoMapMac project in visual studio, you need Xamarin Studio on a Mac. This is because with MonoMac you make a fully native app. With this project type everything is packaged into a .app file.
  1. I am not using XWT as per P_h_i_l's suggestion above, and so these controls will not work on Linux. (We could still use some XWT if we want)
  2. The whole MapCore class is my attempt to not duplicate the core code of the map. It's hard to use inheritance when you have to inherit either the UserControl class or NSView class. I'll take a look at aggregation.
Developer
Aug 6, 2014 at 5:24 PM
And compiled DemoMapMac can't work in Windows too? Then we need to move out that project into other solution.
  1. Ok, let's rename it to DotSpatial.Controls.MonoMac. The other assemblies will be SymbologyForms.MonoMac, ProjectionForms.MonoMac, etc.
  2. Then we not need these MapCore classes. Some code duplications is allowed, e.g. for properties and methods names. but internally we should use aggregation. At least we can move the most of all public methods from IMap\IBasicMap into extensions class helper and use it inside both WinForms and Mac assemblies.