如何在Android上使用Spatialite和Xamarin

时间:2013-12-06 08:33:31

标签: android sqlite xamarin spatialite

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

1 个答案:

答案 0 :(得分:9)

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

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

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

    这应该会为捆绑的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;
        }
    }