如何在Android上使用Xamarin的Spatialite

我想在Android上使用Spatialite而不是使用Xamarin的普通SQLite来pipe理和显示地理数据。 内置的SQLite不允许加载扩展。 我该怎么做?

简短的回答:您需要像使用其他NDK库一样使用自定义的SQLite作为Android本机库。 棘手的部分是为数据库获得有用的C#API。 Xamarin文档似乎只有非常简单的单一方法API指南。

因为我比Java更熟悉Java,所以我使用了Android Java库(.jar)和Android本地库(.so)的组合。 Android Java库已经为数据库提供了Java API包装器,它与通常的Android Java应用程序中可以使用的包装器完全相同。 当然,从C#技术上直接访问本地库也是可能的,所以java / jar可以排除在故事之外。 如果你知道这个好的工具,请告诉我。

  1. 为Xamarin创build.jar绑定项目,将其添加到与Android项目相同的解决scheme中
  2. 将jsqlite.jar添加到绑定项目的Jars文件夹中。 从这里获取: jsqlite.jar
  3. 将本机库二进制文件(libjsqlite.so和libproj.so)添加到您的应用程序项目中,为此创build文件夹libs / armeabi。 从Nutiteq AdvancedMap3D项目获取这些
  4. 将.so文件定义为AndroidNativeLibrary,并将Copy设置为Output Directory
  5. 修复绑定定义以删除构build错误。 将以下内容添加到绑定项目的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> 

这应该产生你工作的C#API捆绑SQLite,包括Spatialite,Proj.4和GEOS 。 jsqlite数据库API本身不同于其他C#SQLite API,你需要使用callback类。 请参阅以下示例要检查模块的版本:

 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); } } 

最后现在查询真实的空间数据,使用Xamarin的Nutiteq 3D Maps SDK对其进行可视化:

 // 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; } }