如何在Android上使用Spatialite和Xamarin

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

简短回答:您需要使用自己定制的SQLite作为Android Native Library,就像使用其他NDK库一样。 棘手的部分是为数据库提供有用而不是那么简单的C#API。 Xamarin docs似乎只有非常简单的单一方法API的指南。

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

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

这应该会为捆绑的SQLite生成C#API,包括Spatialite,Proj.4和GEOS 。 jsqlite DB API本身与其他C#SQLite API不同,您需要使用回调类。 请参阅以下示例要检查模块的版本:

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

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

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