How to use Spatialite with Xamarin on Android

I want to use Spatialite instead of plain SQLite with Xamarin on Android to manage and display geographic data. Built-in SQLite does not allow loading extensions. How can i do this?

+7
android sqlite xamarin spatialite
source share
1 answer

Short answer: you need to use your own custom SQLite as the base Android library, for example, with other NDK libraries. The tricky part is getting a useful less trivial C # API for the database. Xamarin docs seem to have guides only for simple, simple APIs.

As I'm already used to Java than .NET, I used a combination of the Android Java library (.jar) and the Android native library (.so). The Android Java library already has a Java API shell for the database; this is exactly the same shell that can be used in regular Java Java applications. Of course, technically direct access to the native library from C # is also possible, so java / jar can be excluded from the history. If you know good tools for this, let me know.

  • Create a jar binding project for Xamarin, add it to the same solution as your Android project.
  • Add jsqlite.jar to the Jars folder of the bindings project. Get it from here: jsqlite.jar
  • Add your own libraries (libjsqlite.so and libproj.so) to your application project, create the libs / armeabi folder for this. Get It From The Nutiteq AdvancedMap3D Project
  • Define the .so files as AndroidNativeLibrary and set Copy to Output Directory
  • Fix binding definitions to remove build errors. Add the project of your bindings to Transforms / Metadata.xml:
<remove-node path="/api/package[@name='jsqlite']/class[@name='Backup']/field[@name='handle']" /> <remove-node path="/api/package[@name='jsqlite']/class[@name='Database']/field[@name='handle']"/> <attr path="/api/package[@name='jsqlite']" name="managedName">jsqlite</attr> 

This should generate your working C # API in the SQLite bundle along with Spatialite, Proj.4, and GEOS . The jsqlite DB API itself is different from other C # SQLite APIs, you need to use callback classes. See the following examples. To check module versions:

 try { db.Open ("/sdcard/mapxt/estonia-latest-map.sqlite", Constants.SqliteOpenReadonly); // show versions to verify that modules are there db.Exec ("SELECT spatialite_version(), proj4_version(), geos_version(), sqlite_version()", new GeneralQryResult ()); } catch (jsqlite.Exception ex) { Log.Error( ex.LocalizedMessage ); } ... // prints query results as text public class GeneralQryResult : Java.Lang.Object, ICallback { public bool Newrow (string[] rowdata) { string row = ""; foreach (var data in rowdata) { row += data + " | "; } Log.Info(row); return false; } public void Types (string[] types) { // never called really } public void Columns (string[] cols){ Log.Debug ("Query result:"); string row = ""; foreach (var col in cols) { row += col + " | "; } Log.Info (row); } } 

Finally, now request real spatial data using the Nutiteq 3D Maps SDK for Xamarin to render it

 // Spatialite query, show results on map // 1. create style and layer for data LineStyle.Builder lineStyleBuilder = new LineStyle.Builder (); lineStyleBuilder.SetColor (NutiteqComponents.Color.Argb(0xff, 0x5C, 0x40, 0x33)); //brown lineStyleBuilder.SetWidth (0.05f); LineStyle lineStyle = lineStyleBuilder.Build (); GeometryLayer geomLayer = new GeometryLayer (view.Layers.BaseLayer.Projection); view.Layers.AddLayer (geomLayer); // 2. do the query, pass results to the layer Database db = new Database (); try { db.Open ("/sdcard/mapxt/estonia-latest-map.sqlite", Constants.SqliteOpenReadonly); // spatial query. Limit to 1000 objects to avoid layer overloading String qry = "SELECT id, HEX(AsBinary(Transform(geometry,3857))), sub_type, name FROM ln_railway LIMIT 1000"; db.Exec (qry, new SpatialQryResult (geomLayer, lineStyle)); } catch (jsqlite.Exception ex) { Log.Error( ex.LocalizedMessage ); } ... // adds query results to given layer, with given style public class SpatialQryResult : Java.Lang.Object, ICallback { GeometryLayer _geomLayer; Style _geomStyle; public SpatialQryResult(GeometryLayer geomLayer, Style geomStyle){ _geomLayer = geomLayer; _geomStyle = geomStyle; } public bool Newrow (string[] rowdata) { string id = rowdata [0]; string geomHex = rowdata [1]; string type = rowdata [2]; string name = rowdata [3]; Label label; if (name != null && name.Length > 1) { label = new DefaultLabel (name, type); } else { label = null; } Geometry[] lineGeoms = WkbRead.ReadWkb(new ByteArrayInputStream(Utils .HexStringToByteArray(geomHex)), rowdata); // following fails if not Line, change for other geometries foreach (Line lineGeom in lineGeoms) { _geomLayer.Add(new Line(lineGeom.VertexList, label, (LineStyle)_geomStyle, _geomLayer)); } return false; } } 
+9
source share

All Articles