zoukankan      html  css  js  c++  java
  • ArcGIS for Android离线数据编辑实现原理

    ArcGIS for Android中现已经提供了离线缓存图片的加载功能,极大的提高了我们访问地图的效率,对于离线的数据编辑暂时还不支持,而现在对于离线数据编辑操作的需求越来越多,那我们如何才能实现离线数据编辑功能呢?下面我们介绍一下,通过sqlite来实现离线数据编辑的解决方案。

        一、离线数据编辑解决方案

        离线编辑无非就是将所需的数据下载到我们的手持端进行存储,当无法连接网络时,数据的读取、显示、编辑、保存都是通过本地数据库完成的;而后,当可以连接网络时,再将编辑的数据从手持端的数据库中提取出来进行在线数据提交。

        二、离线数据编辑实现步骤

        根据上面的流程图我们来分析一下,实现离线数据编辑的步骤:

        1、 通过ArcGIS Server来发布一个FeatureService服务用于数据下载或上传;

        2、 手持端编写通过服务请求业务数据的代码;

        在手持端编写下载数据的代码,通过FeatureService服务进行数据的下载,代码如下:

    Query query = new Query();

    query.setOutFields(new String[] {"*" });                 query.setInSpatialReference(featureLayer.getSpatialReference());

    query.setWhere("objectid in ("+where+")");

    flayer.selectFeatures(query, ArcGISFeatureLayer.SELECTION_METHOD.NEW,new CallbackListener<FeatureSet>() {                     

                        publicvoid onError(Throwable e) {

                                    }

                        publicvoid onCallback(FeatureSet queryResults) {               

                        }

                      });

        3、 在手持端创建数据库和业务数据表;

        在手持端中,我们需要数据库来存储业务数据,因此我们需要创建Sqlite数据库和相应的业务表。

    在创建数据库时,我们可以通过扩展Android的帮助类SQLiteOpenHelper来维护和操作数据库和表,创建的表要与我们下载的业务数据的表结构一致。

    public class DatabaseHelper extends SQLiteOpenHelper {

       public static final String SYSTEM_TABLE = "sqlite_master";

       private static final int VERSION=1;

       public static final String TABLEORFIELDS =  "tableOrfields";         

       public static final String TABLENAME = "Environment";       

       public DatabaseHelper(Context context, String name, CursorFactory factory,

                               int version) {

                   super(context, name, factory, version);

                   // TODO Auto-generated constructor stub

       }        

       public DatabaseHelper(Context context, String name){

                   this( context,  name,VERSION);

       }

       public DatabaseHelper(Context context, String name,int version){

                   this(context,name,null,version);

       }

       @Override

       public void onCreate(SQLiteDatabase db) {

                   //创建的表结构

                   String sql = "CREATE TABLE IF NOT EXISTS  "+TABLENAME+"(OBJECTID INTEGER,CODE INTEGER,省名 TEXT,县区名 TEXT,土地覆盖 TEXT, SHAPE TEXT, MARK INT) ";

                   db.execSQL(sql);

       }

       @Override

       public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

                   // TODO Auto-generated method stub

       }

    }

     

         4、 将下载的数据存储到创建的数据库中;

        将第2步下载的要素数据,储存到事先创建好的数据库中,要素的属性我们可以直接存储到我们创建的表中,由于sqlite数据库不支持空间数据类型,因此对于要素的空间几何的存储成为要点,对此,我们可以将空间几何解析拼凑成定制的字符串进行存储,我们可以按照WKT格式拼字符串,如,点:point ( 10.05 10.28 ),线:linestring ((10.0510.28 , 20.95 20.89 )),面:polygon ((10 10, 10 20, 20 20, 20 15, 10 10))。

        将空间几何对象转成WKT的核心代码如下:

    /**

          *将几何对象生成wkt字符串

          */

        publicstaticString GeometryToWKT(Geometry geometry){    

             if(geometry ==null){

                  returnnull;

              }

              String geoStr ="";

              Geometry.Type type = geometry.getType();

             if("Point".equals(type.name())){           

                   Point pt = (Point)geometry;

                   geoStr = type.name()+"("+pt.getX() +" "+pt.getY()+")";         

              }elseif("Polygon".equals(type.name()) ||"Polyline".equals(type.name())){

                   MultiPath pg = (MultiPath)geometry;

                   geoStr = type.name()+"("+"";

                       intpathSize = pg.getPathCount();

                  for(intj=0;j<pathSize;j++){

                        String temp ="(";

                       intsize = pg.getPathSize(j);              

                       for(inti=0;i<size;i++){                   

                             Point pt = pg.getPoint(i);

                             temp +=pt.getX() +" "+pt.getY()+",";

                        }

                        temp = temp.substring(0, temp.length()-1)+")";

                        geoStr +=temp+",";

                   }

                   geoStr = geoStr.substring(0, geoStr.length()-1)+")";

              }elseif("Envelope".equals(type.name())){

                   Envelope env = (Envelope)geometry;

                   geoStr = type.name()+"("+ env.getXMin() +","+env.getYMin()+","+env.getXMax()+","+env.getYMax()+")";         

              }elseif("MultiPoint".equals(type.name())){          

              }else{

                   geoStr =null;

              }       

             returngeoStr;

         }

        将查询的数据插入到sqlite的核心代码:

    //数据批量插入

             privatevoidinsertGraphics(Graphic[] gps){

                  //开启事务,使用事务为了提高数据操作的效率

                  db.beginTransaction();

                  for(Graphic gp : gps){                

                        ContentValues values =newContentValues();

                        String[] attributs = gp.getAttributeNames();

                       for(String att : attributs){

                             Object attStr = gp.getAttributeValue(att);

                             String temp ="";

                            if(attStr ==null){

                                

                             }else{

                                  temp = gp.getAttributeValue(att).toString();

                             }

                             values.put(att,temp );                     

                        }

                        Geometry gt = gp.getGeometry();

    String wktStr = GeometryToWKT(gt);

                        values.put("shape",wktStr);                                                       values.put("mark", 0);

                       db.insert(DatabaseHelper.TABLENAME,null, values);             

                   }

                  db.setTransactionSuccessful();//设置事务成功

                  db.endTransaction();//结束事务

        插入数据时启用Sqlite的事务机制,可以提高批量插入数据的速度。

        5、 离线状态时,查询手持端数据库中的数据在设备上展示;

        当我们要显示数据时,可以将数据库的数据查询出来,将查询的每条记录生成空间要素对象属性,将我们上面拼凑的字符串反解析生成空间要素对象,也就是将WKT数据格式解析成为一个空间要素对象。

        将WKT数据字符串解析成空间几何对象的核心代码:

    /**

          *wkt字符串拼成几何对象

          */

        publicstaticGeometry WKTToGeometry(String wkt){

              Geometry geo =null;

             if(wkt ==null|| wkt ==""){

                  returnnull;

              }

              String headStr = wkt.substring(0, wkt.indexOf("("));

              String temp = wkt.substring(wkt.indexOf("(")+1, wkt.lastIndexOf(")"));

             if(headStr.equals("Point")){

                   String[] values = temp.split(" ");

                   geo =newPoint(Double.valueOf(values[0]),Double.valueOf(values[1]));

              }elseif(headStr.equals("Polyline") || headStr.equals("Polygon")){

                   geo =parseWKT(temp,headStr);

              }elseif(headStr.equals("Envelope")){

                   String[] extents = temp.split(",");             

                   geo =newEnvelope(Double.valueOf(extents[0]),Double.valueOf(extents[1]),Double.valueOf(extents[2]),Double.valueOf(extents[3]));

              }elseif(headStr.equals("MultiPoint")){         

              }else{

                  returnnull;

              }

             returngeo;

         }       

        privatestaticGeometry parseWKT(String multipath,String type){

              String subMultipath = multipath.substring(1, multipath.length()-1);

              String[] paths;

             if(subMultipath.indexOf("),(") >=0 ){           

                   paths = subMultipath.split("),(");//多个几何对象的字符串

              }else{

                   paths =newString[]{subMultipath};

              }

              Point startPoint =null;

              MultiPath path =null;

             if(type.equals("Polyline")){

                   path =newPolyline();

              }else{

                   path =newPolygon();

              }       

             for(inti=0;i<paths.length;i++){

                   String[] points = paths[i].split(",");

                   startPoint =null;

                  for(intj=0;j<points.length;j++){               

                        String[] pointStr = points[j].split(" ");

                       if(startPoint ==null){

                             startPoint =newPoint(Double.valueOf(pointStr[0]),Double.valueOf(pointStr[1]));

                             path.startPath(startPoint);

                        }else{                      

                             path.lineTo(newPoint(Double.valueOf(pointStr[0]),Double.valueOf(pointStr[1])));

                        }                 

                   }            

              }

             returnpath;

         }

     

    6、 在设备上编辑要素数据;

        对于要素的编辑可以分为两方面,一方面是属性的编辑,另一方面是空间编辑,对于这两方面的操作,官方提供的例子中提供了类似的功能(AttributeEditor和GeometryEditor两个例子)。

    1)属性编辑

    对于属性修改无非就是对Graphic对象的中的属性进行修改, Graphic不能添加事件监听,所以不能像按钮一样,添加一个点击事件弹出该要素的相关信息,我们可以通过GraphicsLayer的getGraphicIDs(float x, float y,int tolerance)方法来获取要素及其要素的相关属性,可悲是Graphic中没提供修改属性的接口,只能新建一个Graphic对象并在他的构造方法中来添加更新后的属性或者是通过GraphicsLayer的updateGraphic(int id,Map<String,Object> attributes)的方法来更新Graphic的属性,并且将修改的要素属性更新到本地sqlite数据库中及其修改表中mark字段的状态。

    2)空间编辑

    对于Graphic对象我们不仅可以改变他的属性还可以修改它的空间位置信息,对于点、线、面的修改略有不同。

    点修改时,直接更新Graphic的Geometry即可,不过Graphic没有提供修改Geometry的接口,我们只能通过GraphicsLayer的updateGraphic(int id,Geometry geometry)方法来实现更新它的空间位置。

    线和面的空间位置改变主要指的是线或面的节点的位置修改,我们可以点击线或面上的一个节点进行拖动用GeometryEngine.getNearestVertex()可以得到我们点击的点,距离几何体的哪个节点最近,并返回一个Proximity2DResult对象,通过这个对象我们可以得到这个节点Index位置,再通过线或面对象的setPoint(int index,Point point)方法更新节点,这时我们的图形就可以改变了,将更新后的Graphic的空间对象重新解析成定义的格式入库及其修改表中mark字段的状态。

    7、 将编辑后的数据更新到设备数据库中;

        将编辑后的数据通过第4步的方式更新到本地数据库中,更新数据的代码如下:

    SQLiteDatabase db = this.getWritableDatabase();

              db.beginTransaction();

              db.update(table, values, whereClause, whereArgs);

              db.setTransactionSuccessful();

              db.endTransaction();

        如果要多次更新时,可以使用sqlite数据库的事务机制,来提高执行效率。

    8、 在线时通过ArcGIS Server的服务提交业务数据;

        通过sqlite查询,将本地数据库中的数据查询出来,并将数据拼成空间要素对象,再通过FeatureService服务将数据更新到图层上。

        更新数据代码如下:

    Graphic[] gps = new Graphic[list.size()];

    gps = list.toArray(gps);

    featureLayerupdate.applyEdits(null, null, gps, null)

        List为Graphic对象的列表,上面的操作是更新操作,如果需要添加或删除只需将Graphic数组放到不同的参数位置上即可。

     

        到此,离线数据编辑操作基本完成,待日后spatialite空间数据库日渐完善后,离线编辑将变的更加简单,期待中。

     

    http://www.verydemo.com/demo_c131_i5607.html

  • 相关阅读:
    EF6 Code First 模式更新数据库架构
    bootstrap-datepicker 插件修改为默认中文
    常用网络资源下载
    jQuery框架学习第十一天:实战jQuery表单验证及jQuery自动完成提示插件
    AngularJS实现原理
    [个人翻译]GitHub指导文件(GitHub Guides[Hello World])
    年后跳槽如何准备?
    前端学数据库之子查询
    Ionic实战 自动升级APP(Android版)
    读书笔记:《HTML5开发手册》Web表单
  • 原文地址:https://www.cnblogs.com/dwf07223/p/3029728.html
Copyright © 2011-2022 走看看