Category Archives: 从这里发布的

从blog.newnaw.com发布的

Portable Basemap Server发布2.0.5版本

  更新内容:
~为数据源类型为ArcGISDynamicMapService的PBS服务增加附加参数设置,比如layers,layerDefs等,达到控制图层可见性,按属性过滤图层内容等目的;
~REST Admin API为ArcGISDynamicMapService数据源增加changeParams操作,以动态修改附加参数。详见这里;
~数据源增加对ArcGISTiledMapService的支持;
~增加自定义在线地图功能。可通过修改CustomOnlineMaps.xml文件内容,自行增删在线地图数据源(数据源须采用Google Maps/Bing Maps/ArcGIS Online地图的缓存策略);
~增加自动保存/载入上次配置的功能;
~为系统托盘图标增加右键菜单;
~修复已知bug;
  详见:https://blog.newnaw.com/?p=890
  再加入一些功能后,Portable Basemap Server将会在下个版本开源。

ArcGIS Runtime性能

  可能你已经了解到了一些要随ArcGIS 10.1版本发布的新产品ArcGIS Runtime的信息,比如它的跨平台,高便携,64位,可扩展等。
  Esri刚刚结束的developer summit上,以WPF为例,演示了最新的prerelease版本中ArcGIS Runtime的性能。视频中prerelease版中同时加载了10,000个带动画的复杂渲染的graphic。

  Web API中一直被偏执狂们诟病的graphic性能有了长足的提升,ArcGIS Runtime更加让人期待了。
  ps:期待更多有社会价值的公交车,出租车,物流监控系统出现。

弟子规-与鲶总、李总、法人共勉

总 叙

弟子规 圣人训 首孝悌 次谨信

泛爱众 而亲仁 有余力 则学文

入 则 孝

父母呼 应勿缓 父母命 行勿懒

父母教 须敬听 父母责 须顺承

冬则温 夏则凊 晨则省 昏则定

出必告 反必面 居有常 业无变

事虽小 勿擅为 苟擅为 子道亏

物虽小 勿私藏 苟私藏 亲心伤

亲所好 力为具 亲所恶 谨为去

身有伤 贻亲忧 德有伤 贻亲羞

亲爱我 孝何难 亲憎我 孝方贤

亲有过 谏使更 怡吾色 柔吾声

谏不入 悦复谏 号泣随 挞无怨

亲有疾 药先尝 昼夜侍 不离床

丧三年 常悲咽 居处变 酒肉绝

丧尽礼 祭尽诚 事死者 如事生

出 则 悌

兄道友 弟道恭 兄弟睦 孝在中

财物轻 怨何生 言语忍 忿自泯

或饮食 或坐走 长者先 幼者后

长呼人 即代叫 人不在 己即到

称尊长 勿呼名 对尊长 勿见能

路遇长 疾趋揖 长无言 退恭立

骑下马 乘下车 过犹待 百步余

长者立 幼勿坐 长者坐 命乃坐

尊长前 声要低 低不闻 却非宜

进必趋 退必迟 问起对 视勿移

事诸父 如事父 事诸兄 如事兄

朝起早 夜眠迟 老易至 惜此时

晨必盥 兼漱口 便溺回 辄净手

冠必正 纽必结 袜与履 俱紧切

置冠服 有定位 勿乱顿 致污秽

衣贵洁 不贵华 上循分 下称家

对饮食 勿拣择 食适可 勿过则

年方少 勿饮酒 饮酒醉 最为丑

步从容 立端正 揖深圆 拜恭敬

勿践阈 勿跛倚 勿箕踞 勿摇髀

缓揭帘 勿有声 宽转弯 勿触棱

执虚器 如执盈 入虚室 如有人

事勿忙 忙多错 勿畏难 勿轻略

斗闹场 绝勿近 邪僻事 绝勿问

将入门 问孰存 将上堂 声必扬

人问谁 对以名 吾与我 不分明

用人物 须明求 倘不问 即为偷

借人物 及时还 后有急 借不难

凡出言 信为先 诈与妄 奚可焉

话说多 不如少 惟其是 勿佞巧

奸巧语 秽污词 市井气 切戒之

见未真 勿轻言 知未的 勿轻传

事非宜 勿轻诺 苟轻诺 进退错

凡道字 重且舒 勿急疾 勿模糊

彼说长 此说短 不关己 莫闲管

见人善 即思齐 纵去远 以渐跻

见人恶 即内省 有则改 无加警

唯德学 唯才艺 不如人 当自砺

若衣服 若饮食 不如人 勿生戚

闻过怒 闻誉乐 损友来 益友却

闻誉恐 闻过欣 直谅士 渐相亲

无心非 名为错 有心非 名为恶

过能改 归于无 倘掩饰 增一辜

泛 爱 众

凡是人 皆须爱 天同覆 地同载

行高者 名自高 人所重 非貌高

才大者 望自大 人所服 非言大

己有能 勿自私 人所能 勿轻訾

勿谄富 勿骄贫 勿厌故 勿喜新

人不闲 勿事搅 人不安 勿话扰

人有短 切莫揭 人有私 切莫说

道人善 即是善 人知之 愈思勉

扬人恶 即是恶 疾之甚 祸且作

善相劝 德皆建 过不规 道两亏

凡取与 贵分晓 与宜多 取宜少

将加人 先问己 己不欲 即速已

恩欲报 怨欲忘 报怨短 报恩长

待婢仆 身贵端 虽贵端 慈而宽

势服人 心不然 理服人 方无言

亲 仁

同是人 类不齐 流俗众 仁者希

果仁者 人多畏 言不讳 色不媚

能亲仁 无限好 德日进 过日少

不亲仁 无限害 小人进 百事坏

余 力 学 文

不力行 但学文 长浮华 成何人

但力行 不学文 任己见 昧理真

读书法 有三到 心眼口 信皆要

方读此 勿慕彼 此未终 彼勿起

宽为限 紧用功 工夫到 滞塞通

心有疑 随札记 就人问 求确义

房室清 墙壁净 几案洁 笔砚正

墨磨偏 心不端 字不敬 心先病

列典籍 有定处 读看毕 还原处

虽有急 卷束齐 有缺坏 就补之

非圣书 屏勿视 蔽聪明 坏心志

勿自暴 勿自弃 圣与贤 可驯致

命自我作 福自我求

心好命又好 富贵直到老

命好心不好 福变为祸兆

心好命不好 祸转为福报

心命俱不好 遭殃且贫夭

心可挽乎命 最要存仁道

命实造於心 吉凶惟人召

信命不修心 阴阳恐虚矫

修心一听命 天地自相保

在新的Android工程中调用已经编译好的so库文件代码

  依旧是Android,NDK,JNI相关的问题。
  照着ndk帮助的说明,运行<ndk>/samples目录下的hello-jni工程后,你一定想知道如何在自己的项目里调用别人已经编译好的.so库文件。于是新建一个Android工程,将hello-jni例子中ndk-build后的结果:libs文件夹(包括其下所有内容)拷贝到新建工程的根目录下,仿照hello-jni的代码,调用native方法:

   1: package com.my.SoTest;

   2:  

   3: import android.app.Activity;

   4: import android.os.Bundle;

   5: import android.widget.TextView;

   6:  

   7: public class SoTestActivity extends Activity {

   8:     /** Called when the activity is first created. */

   9:     @Override

  10:     public void onCreate(Bundle savedInstanceState) {

  11:         super.onCreate(savedInstanceState);

  12:         

  13:         TextView tv=new TextView(this);

  14:         tv.setText(stringFromJNI());

  15:         setContentView(tv);

  16:     }

  17:     public native String stringFromJNI();

  18:     static {

  19:         System.loadLibrary("hello-jni");

  20:     }

  21: }

  运行后,logcat记录以下错误:

No implementation found for native Lcom/my/SoTest/SoTestActivity;.stringFromJNI ()Ljava/lang/String;

java.lang.UnsatisfiedLinkError: stringFromJNI

  在hello-jni的代码注释里明确说明,UnsatisfiedLinkError错误是由于native代码中没有该方法的实现而引起的,但stringFromJNI这个方法,在c代码中是有的;前一个错误就比较明确了,是在Lcom/my/SoTest/SoTestActivity中去找这个方法实现的,打开hello-jni.c文件看看该函数头,其中有这样的字样:Java_com_example_hellojni_HelloJni_stringFromJNI,而com.example.hellojni恰好是例子工程的package name,HelloJni则是class的名称。也就是说,我们.so中函数声明涉及到的package name和class name与调用它的package name和class name不符。google一下印证了这个猜测,但提供现成解决办法的不多,总不能自己工程的package name必须和.so里的一样吧。
  搜索看到了一篇NDK的教程,Using NDK to Call C code from Android Apps(需翻墙),里面写明了在一个普通Android工程中,如何从零开始组装自己的native代码。大致的步骤是,首先写一个java类,里面声明一些需要实现native方法(函数签名即可),然后用javah命令来生成对应的header文件,里面的内容是自动生成的native函数声明,然后照着这个声明实现各个函数,最后编译,用最初的java类调用。
  受此启发,我们在拿到别人已经编译好的.so文件后,可首先新建一个java类,所在package的名称和class名称都与.so文件中函数签名提示的一致,在这个类中加入native方法的声明。这样在别处就可以用这个wrapper调用so库中的函数了。

在Android上利用NDK编译并使用Spatialite库(Windows环境)

  Sqlite数据库因其体积便携(基于单个文件,跨平台),功能完整(类似RDBMS)受到非常广泛的欢迎,在iOS,Android平台上都提供原生的支持。Spatialite是Sqlite的一个空间扩展,根据官方介绍,Spatialite之于Sqlite,相当于PostGIS之于PostgreSQL。
  Spatialite扩展提供了一系列用于空间操作的SQL函数,例如基础的空间查询,缓冲区生成等,也有高级的类似ArcObjects中ITopologicalOperator,IRelationalOperator等接口的功能。
  在Android平台上使用Spatialite资料比较少,主要是google上的Spatialite for Android和github上的android-spatialite两个工程。前者带有完整的编译步骤,我尝试了这个,其中遇到了些问题,记录下来,方便需要用到的朋友。后者工程似乎带有编译好的libjsqlite.so库,可直接使用。
  我机器的环境是Windows 7 64bit,已经搭建好了Android Eclipse开发环境。由于Spatialite及Sqlite是C/C++的native语言编写,而Android的默认开发语言是Java,所以在Android上编译Spatialite必须用到google提供的NDK(Native Development Kit)环境。NDK环境的搭建比较简单,按照google的文档进行即可:

  1. 下载NDK压缩包(android-ndk-r7b-windows.zip)。我下载的是最新的android-ndk-r7b版本。将其解压到任意目录,下文以<ndk>指代。注意:由于后面cygwin中配置环境变量时不支持带空格的路径,所以建议将其解压到某个盘符的根目录下,比如d:android-ndk-r7b;
  2. 设置Windows的环境变量。主要是将NDK的路径添加到PATH中去,方便在任何位置调用ndk-build命令。详细操作不再赘述。此步骤只是为cmd提供了便利;
  3. 由于NDK需求中说明,必须用到GNU Make命令,所以在Windows环境上还需要提前安装Cygwin。这是在Windows上模拟Linux环境的工具。安装步骤参考此贴:Windows环境下Android NDK环境搭建。注意在cygwin的linux环境下,配置$NDK环境变量,方便在任何地方调用ndk-build编译命令。我在安装Cygwin时,只额外勾选了Devel库。

  完成这个步骤后,可尝试编译运行NDK中自带的hello-jni例子,验证NDK编译环境已经搭好。具体步骤参见google的文档。可以发现,native代码需要放在与src同级的jni目录下,该目录下必须至少有Android.mk文件,向NDK系统描述源文件;编译时需要在工程的根目录下运<ndk>/ndk-build命令;利用NDK编译后,Android工程文件夹中会多出libs目录,里面会生成lib<something>.so共享库,这就是我们要用到类库,该文件也会一并打包到apk文件中去。更多的ndk知识请参考<ndk>/documentation.html文件,非常详尽。注:JNI相当于.net平台的p/invoke。
  接下来就要按照Spatialite for Android上的提示,在cygwin环境中编译spatialite库了。

  1. 在windows下利用svn工具对整个project checkout(这次作者用的是Spatialite 3.0.1,PROJ 4.7.0,geos 3.2.2几个库)。会发现有两个工程spatialite-android和spatialite-android-test。我们主要对spatialite-android工程进行编译;
  2. 由于spatialite用到了geos和proj两个库,所以首先需要为该工程下载,配置两个库。具体步骤如下,在cygwin环境中执行:
       1: $ cd jni

       2: $ wget http://download.osgeo.org/geos/geos-3.2.2.tar.bz2

       3: $ tar xvjf geos-3.2.2.tar.bz2

       4: $ cd geos-3.2.2

       5: $ ./configure --build=x86_64-pc-linux-gnu --host=arm-linux-eabi

       6: $ cd ..

       7: $ wget ftp://ftp.remotesensing.org/proj/proj-4.7.0.tar.gz

       8: $ tar xvzf proj-4.7.0.tar.gz

       9: $ cd proj-4.7.0

      10: $ ./configure --build=x86_64-pc-linux-gnu --host=arm-linux-eabi

      11: $ touch src/empty.cpp

      12: $ cd ..

  3. 按照作者的提示,下来就可以直接对工程进行ndk-build了(注:此时仍然在spatialite-android/jni目录下,而非spatialite-android目录)。如果你这么做了,会出现一个错误:
       1: ...

       2: Compile thumb  : proj <= rtodms.c

       3: Compile thumb  : proj <= vector1.c

       4: StaticLibrary  : libproj.a

       5: Compile++ thumb  : geos <= geos_c.cpp

       6: In file included from D:/spatialite-android/jni/geos-3.2.2/source/headers/geos/geom/Coordinate.h:20,

       7:                  from D:/spatialite-android/jni/geos-3.2.2/source/headers/geos/g                                                                                                                eom/Envelope.h:26,

       8:                  from D:/spatialite-android/jni/geos-3.2.2/source/headers/geos/i                                                                                                                ndex/strtree/STRtree.h:27,

       9:                  from D:/spatialite-android/jni/geos-3.2.2/capi/geos_c.cpp:19:

      10: D:/spatialite-android/jni/geos-3.2.2/source/headers/geos/platform.h:29:20: error                                                                                                                : ieeefp.h: No such file or directory

      11: /cygdrive/d/android-ndk-r7b/build/core/build-binary.mk:243: recipe for target `/                                                                                                                cygdrive/d/spatialite-android/obj/local/armeabi/objs-debug/geos/geos-3.2.2/capi/                                                                                                                geos_c.o' failed

      12: make: *** [/cygdrive/d/spatialite-android/obj/local/armeabi/objs-debug/geos/geos                                                                                                                -3.2.2/capi/geos_c.o] Error 1

    image
    提示ieeefp.h: No such file or directory。要修正这个错误,你需要在spatialite-android/jni/geos-3.2.2/source/headers/geos/platform.h文件中大约15行的位置,找到“#define HAVE_IEEEFP_H 1”这句话,并在其下面添加一行“#undef HAVE_IEEEFP_H”,取消对HAVE_IEEEFP_H的定义。保存该文件;

  4. 再次ndk-build,这回发现上面的错误消失了。但又产生了新的错误:
       1: ...

       2: Compile++ thumb  : geos <= InteriorPointLine.cpp

       3: Compile++ thumb  : geos <= InteriorPointPoint.cpp

       4: Compile++ thumb  : geos <= LineIntersector.cpp

       5: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp: In st                                                                                                                atic member function 'static double geos::algorithm::LineIntersector::interpolat                                                                                                                eZ(const geos::geom::Coordinate&, const geos::geom::Coordinate&, const geos::geo                                                                                                                m::Coordinate&)':

       6: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp:224: e                                                                                                                rror: expected unqualified-id before '(' token

       7: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp:232: e                                                                                                                rror: expected unqualified-id before '(' token

       8: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp: In me                                                                                                                mber function 'void geos::algorithm::LineIntersector::computeIntersection(const                                                                                                                 geos::geom::Coordinate&, const geos::geom::Coordinate&, const geos::geom::Coordi                                                                                                                nate&)':

       9: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp:304: e                                                                                                                rror: expected unqualified-id before '(' token

      10: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp:306: e                                                                                                                rror: expected unqualified-id before '(' token

      11: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp: In me                                                                                                                mber function 'int geos::algorithm::LineIntersector::computeIntersect(const geos                                                                                                                ::geom::Coordinate&, const geos::geom::Coordinate&, const geos::geom::Coordinate                                                                                                                &, const geos::geom::Coordinate&)':

      12: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp:426: e                                                                                                                rror: expected unqualified-id before '(' token

    image在很多位置都提示 error: expected unqualified-id before ‘(‘ token,google一下这个错误,发现大概是由于在头文件中对某个函数重复声明冲突引起的。于是打开spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp文件查看,发现基本都与一个名为“ISNAN”的函数有关。在Windows下搜索包含此字符串的文件,又来到了刚才修改过的platform.h文件中,发现这个文件中有与此函数相关的语句。为了解决此问题,尝试对此文件进行再次修改。在大约25行的位置,可以找到“/* #undef HAVE_ISNAN */”这句话,在这句话下面添加一句“#define HAVE_ISNAN 1”。这样做的目的是改变该文件后续的一些编译处理。

  5. 最后再次ndk-build,成功输出libjsqlite.so库。
    image

  现在我们在eclipse中运行spatialite-android这个工程(需要将test-2.3.sqlite数据库adb push到模拟器的sd卡上),就可以看到预期的结果了。
image  关于如何利用java语言调用spatialite的功能,可以详细参考这个工程中jsqlite包下的封装代码。至此我们就完成了在Android平台上使用spatialite库的准备工作。
  此次编译中遇到的问题可参考:http://code.google.com/p/spatialite-android/issues/detail?id=3

心力

  心力,可以理解为和通常所讲的“体力”相对而言。是指一个人心理的力量,精神上的能量,或者意念。当然你不可能用意念杀死别人,但你可以用心力拯救你自己。
  从前有一片茫茫大海,比地球上所有大陆都变成海洋后的海洋还要大。这片海面上漂浮着唯一一个木环,直径只有杯口大小;海里面有一只乌龟,双目失明,它一直在水中游动,一万年才浮到水面上呼吸一次。
  我们每个人的生命是有限的,但内心是永恒的。今生今世只不过是内心灵魂的一段短暂旅程,现在的身体只是它寄存的一种形式。上辈子你有可能是只猪,是只考拉,甚至阿凡达,下辈子你有可能是格桑花,是面包树。这些表现形式都不真正属于你,生不带来死不带去,只有内心才是真正的你。但这辈子你能获得这个人身,几率和上面的盲龟浮出海面时,头恰好能钻进木环的几率是一样的。之所以能这么幸运,是前几百世修来的福气。
  每做一件好事或者一件坏事,都是你自己的“业”,好的业力多了,就会有好报。不管你在哪一世,万般带不走,唯有业随身。我们活着,除了要积攒业力,最重要的就是锻炼心力,使自己变得正直,善良,坚韧不拔,温柔如水。
  路上碰到瘸腿的乞丐了,不要鄙视,给些零钱,他们或许就能多吃一碗救命的米饭;拥挤的地铁上碰到不讲卫生的农民工了,不要移动脚步躲开,他们和你一样有父母有儿女,他们盖起的高楼能让你们安居乐业,而你们做出的价值千万的系统也许没有一点实用价值,只是在浪费纳税人的血汗钱,为腐败提供了温床。
  锻炼心力是需要决心和勇气的,但可以从一点一滴做起,比如早晨不赖床,或者开始学一些对人生有意义的东西。最好的办法就是挫折和困难。菩萨和天使不总是带给你好处,还可能是挫折和困难,越是逆境,就越要珍惜。因为在你得到这个磨练机会的时候,别人可能还在把时间挥霍在享乐上。享乐的时间和心力的积累通常是成反比的。
  有一些人等意识到,可能也就心有余,力不足了。与各位共勉。

ArcGIS移动客户端中可以自动离线的底图图层

  我们都知道,在使用ArcGIS移动客户端(已更名为ArcGIS Runtime SDK for iOS/Windows Phone/Android)API进行开发时,一般对于底图数据,需要用ArcGISTiledMapServiceLayer这个类来加载,指向一个在线的缓存地图服务。而对于移动GIS应用,通常我们又有非常强烈的离线需求。目前来说,ArcGIS移动客户端底图离线有两种实现办法:自定义图层加载离线数据或等待新版本api来加载10.1的TilePackage底图离线包(iOS API已经实现)。
  这次我们来讨论另一种可以让底图离线的办法。在手机上使用过离线地图程序的朋友都知道,它们一般都会提供自动离线的功能,即当我们连接到互联网,浏览在线地图时,程序会自动将浏览过的数据缓存到本地,以便下次没有网络环境时可离线使用。新版本的Goolge Maps移动版,Android上的RMaps,OruxMaps等程序都有类似的功能。
  那么如何为ArcGIS的移动程序实现类似的功能呢?因为这个图层既要能够加载在线的地图服务,又要能够自动离线,所以我们选择创建一个派生自ArcGISTiledMapServiceLayer的类。当有网络连接时,我们浏览地图的过程中,将切片自动存储到本地,没有网络连接时就可以浏览这些切片了。而且可以给用户提供若干选项,比如是否允许自动下载缓存切片,是否允许自动更新已有的缓存切片等。
  请注意本文讨论内容与之前讨论的自定义图层加载离线数据不同,前者具有自动下载缓存切片的功能,下载后没有网络连接的情况下会自动变成离线图层加载离线数据,有网络连接的情况下可当做正常的在线图层使用;后者只能当做离线图层使用,不能自动下载缓存切片。
  这里我以Windows Phone为例,写好了一个扩展类:OfflineableTiledMapServiceLayer(下载地址见最后)。下面对它进行一些说明。
功能:
  OfflineableTiledMapServiceLayer是一个继承自ArcGISTiledMapServiceLayer的自定义类,在线浏览地图的过程中,会自动将缓存切片保存在Sqlite数据库文件中(存储在应用程序的IsolatedStorage空间内),无需人工干预;这样在没有网络连接的情况下,就自动加载先前保存过的切片,作为离线图层使用(程序代码无需做任何修改)。

如何使用:
  与ArcGISTiledMapServiceLayer用法一致,只需额外设置一些参数即可。
<esri:Map x:Name=”map1″>
    <my:OfflineableTiledMapServiceLayer Url=”http://services.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer” EnableOffline=”True”  SaveOfflineTiles=”True” SaveTilesMode=”SaveOnly”
DeleteSavedOfflineTiles=”False” LoadOfflineTileFirst=”True” />
</esri:Map>

工作流程:
  除了ArcGISTiledMapServiceLayer已有的功能外,OfflineableTiledMapServiceLayer可在浏览地图的过程中,自动将缓存切片保存到Sqlite数据库中。当下次加载该服务时(以URL地址识别),就可从离线的数据库中加载切片数据(没有网络连接的情况下)。
  OfflineableTiledMapServiceLayer目前有以下5个属性可设置:

  • EnableOffline: 默认为True。当设置为True时,OfflineableTiledMapServiceLayer具有自动离线的能力;当设置成False时,OfflineableTiledMapServiceLayer完全和ArcGISTiledMapServiceLayer一样。
  • SaveOfflineTiles: 默认为True。当设置为True时,OfflineableTiledMapServiceLayer会将浏览过的缓存切片保存到本地的Sqlite数据库中(存储在应用程序的IsolatedStorage空间);当设置为False时,OfflineableTiledMapServiceLayer不会保存任何缓存切片。只有当LoadOfflineTileFirst==false时才会生效。
  • LoadOfflineTileFirst: 默认为False。当设置为True时,OfflineableTiledMapServiceLayer会优先从本地Sqlite数据库中加载缓存切片(即使有Internet连接),如果处于离线状态,则会直接从本地Sqlite数据库中加载缓存切片(如果之前没有保存过任何切片,则会抛出异常);当设置为False时,则会优先从在线服务中加载缓存切片,如果处于离线状态,则只能从本地Sqlite中加载数据。
  • SaveTilesMode: 默认为’SaveOnly’。当设置为’SaveOnly’时,OfflineableTiledMapServiceLayer只会存储新的缓存切片(Sqlite数据库中没有的);当设置为’SaveOrUpdate’时,OfflineableTiledMapServiceLayer会存储新的缓存切片,并且更新已有的缓存切片。当在线的缓存地图服务内容更新时,此选项比较有用。只有当LoadOfflineTileFirst==false && SaveOfflineTiles==true时有效。
  • DeleteSavedOfflineTiles: 默认为False。当设置成True时,会首先删除Sqlite数据库中已有的本图层数据,之后重新初始化本图层。只有当EnableOffline==True时有效。

Sqlite文件结构:
  Sqlite在移动设备上具有广泛的应用基础,iOS和Android对其均提供原生支持。OfflineableTiledMapServiceLayer使用Sqlite Client for Windows Phone来读写Sqlite数据库。可以使用Windows Phone 7 Isolated Storage Explorer或者Isolated Storage Explorer Tool将Sqlite文件从Windows Phone的IsolatedStorage中导出,以供其它移动程序使用,比如ArcGIS Runtime SDK for iOS/Android。
  OfflineableTiledMapServiceLayer创建的Sqlite数据库名称为”OfflineTiles.db”,它的内容由一张或多张表组成。

  • ‘MapServices’表:此表是OfflineableTiledMapServiceLayer必须使用的。具有四个字段:’url'(text), ‘spatialreference'(text), ‘fullextent'(text), ’tileinfo'(text)。存储在Sqlite中不同的OfflineableTiledMapServiceLayer由其Url属性区分(假设不同的Url代表不同的缓存地图服务)。另外三个字段分别以JSON格式存储了缓存服务对应的信息,这些信息是在离线状态下初始化图层所需要的。创建该表的SQL语句: CREATE TABLE “MapServices” (“url” TEXT PRIMARY KEY  NOT NULL  UNIQUE , “spatialreference” TEXT NOT NULL , “fullextent” TEXT NOT NULL , “tileinfo” TEXT NOT NULL )
  • 其它表:如果Sqlite中存储过任何OfflineableTiledMapServiceLayer缓存图片,则除了上述MapServices表外还会有其它表。其余每张表以该缓存地图服务的Url字符串命名。这些表包含有4个字段:’level'(integer), ‘row'(integer), ‘column'(integer), ’tile'(blob)。一目了然。创建该表的SQL语句:CREATE TABLE “HereIsYourServiceURL” (“level” INTEGER NOT NULL , “row” INTEGER NOT NULL , “column” INTEGER NOT NULL , “tile” BLOB NOT NULL ), 创建索引的SQL语句: CREATE UNIQUE INDEX ‘idx_ HereIsYourServiceURL ‘ ON ‘ HereIsYourServiceURL ‘ (‘level’ ASC, ‘row’ ASC, ‘column’ ASC)

OfflineableTiledMapServiceLayer的下载地址(包括源码,示例程序,示例Sqlite离线文件):http://www.arcgis.com/home/item.html?id=d2b40d7f553947a2b575556b057f5dcf

解读ArcGIS Runtime SDKs

  本文试图解读新的ArcGIS Runtime SDKs及其本质,与ArcGIS移动SDK,for iOS/Windows Phone/Android,之间的关系,以及这三种移动SDK后续发展的一些猜想。
  ArcGIS Runtime SDKs是随ArcGIS 10.1 beta一起发布的一套横跨桌面和移动端的,跨平台,轻量级的GIS开发SDK的总称。
image

  从上图中我们可以看出,ArcGIS Runtime SDKs家族包括了以下内容:

  • ArcGIS Runtime SDK for Android
  • ArcGIS Runtime SDK for iOS
  • ArcGIS Runtime SDK for Windows Phone
  • ArcGIS Runtime SDK for Windows Mobile
  • ArcGIS Runtime SDK for Java
  • ArcGIS Runtime SDK for Qt
  • ArcGIS Rutnime SDK for WPF

  之所以说ArcGIS Runtime SDKs横跨桌面和移动端,是因为它既包含了iOS/Windows Phone/Android等移动平台的开发SDK,也包含了可以开发传统桌面程序(C/S程序)的WPF、Java、QT等SDK;而后三种SDK则可开发出Windows和Linux平台下的具有丰富交互效果和良好体验的应用程序。其实ArcGIS Runtime还包括了一些现成的应用程序,比如iOS/Windows Phone/Android各自市场上都能下载到的“ArcGIS”程序,ArcGIS Mobile中的“ArcGIS”程序等。
  说起轻量级,首先要看看ArcGIS 10.1产品架构的一些变化。

image

  ArcGIS 10.1中,产品的划分更加明确和简单。主要分为四个部分,桌面GIS(传统的ArcMap,ArcGlobe等),ServerGIS(全新架构的ArcGIS Server),轻量级GIS(ArcGIS Explorer,Runtime SDKs及其对应的应用程序)和ArcGIS Online。前三部分都是围绕ArcGIS Online这个云GIS平台的,在不同程度上都与ArcGIS Online有交互,或可将数据直接部署上去,或作为它的客户端(云+端)。而轻量级GIS就是为了能够在任何地点,任何平台,任何设备上访问云平台提供的GIS能力。
  ArcGIS Runtime SDKs正是在这种背景下诞生的。其实它也分为狭义和广义上的两种解释。狭义的ArcGIS Runtime SDK是指桌面上的WPF,Java和Qt,它们的消息早在半年前就已经流出。它们的出现,是为了逐步替代强大而相对臃肿的ArcGIS Engine这个产品。做过Engine开发的朋友都知道,即使是最简单的显示地图的需求,理论上都必须在客户机上安装ArcGIS Engine Runtime这个运行时(注意不是ArcGIS Runtime),安装包通常400m左右。而利用新的ArcGIS Runtime SDK for WPF/Java/Qt开发出的程序,完全是绿色程序,不需要在客户机上安装任何部件(.Net Framework和JRE不计)即可运行,因为所有的依赖库直接和程序拷贝在一起即可。如果你喜欢ArcGIS Runtime SDK开发的程序的部署过程——拷贝到u盘里/插入目标计算机/运行,那么你一定也喜欢它的卸载过程——关闭程序/拔掉u盘。
  广义上的ArcGIS Runtime SDKs是上述列表中,诸多SDKs的统称。除了包含狭义的ArcGIS Runtime SDK,可以看到加入了移动端部分:ArcGIS Runtime SDK for iOS/Windows Phone/Android/Windows Mobile,而它们其实是新瓶装旧酒,分别对应以前的ArcGIS SDK for iOS/Windows Phone/Android和ArcGIS Mobile,只是换了产品名称而已。为什么会换名称?为什么还有ArcGIS Mobile这个“另类”的东西?这得从ArcGIS Runtime SDK的功能说起。
  做过ArcGIS Web API(ArcGIS API for Javascript/Flex/Silverlight)开发的朋友,应该可以很快理解狭义Runtime这个产品的所有功能。目前ArcGIS Runtime的功能与ArcGIS Web API,ArcGIS移动API(iOS/Windows Phone/Android)基本相同,都是基于ArcGIS REST API的。比如地图服务(动态/缓存)的加载,GraphicsLayer/FeatureLayer,基于FeatureService的数据编辑,Identify/Find/Query操作,GeometryService,Geoprocessing Service的调用等。它们的开发思路和代码编写几乎是一样的,比如ArcGIS API for Silverlight和目前的ArcGIS Runtime SDK for WPF,如出一辙(后者的前身就是ArcGIS API for WPF)。但狭义上的ArcGIS Runtime SDK与ArcGIS Web API不同之处在于,前者可以加载本地数据,包括Map Package/Tile Package/Locator Package/Geoprocessing Package。Map Package是包括.mxd文档和所有引用数据在内的压缩包,其余类似。最早的Package是9.3.1产品中的LayerPackage,它不仅包含了.lyr图层配置信息,还打包了图层所引用的实际数据。早期的ArcGIS Online平台允许用户上传LayerPackage以便分享,现在看来,直到此时Runtime产品的出现才将Package的概念发扬光大,并且为以后的所谓云GIS提供了更多的数据共享途径。对本地数据的读取是否破坏了基于ArcGIS REST API的框架呢?其实没有。ArcGIS API for WPF/Java/QT中,都内置了一个c++写的Web Server,读取本地数据后,会自动发布成这个Web Server上的REST服务来供Runtime SDK使用,所以一切功能,还是由REST API提供的。收起题外话,ArcGIS Runtime SDK的框架,是针对轻量级GIS产品(不包括ArcGIS Web API)的,它拥有统一的编程模型,可以用一致的开发思路,做出C/S,及移动端应用(Web API开发出B/S应用),为开发人员提供了极大的便利。这也是为什么将它们统称为ArcGIS Runtime SDKs的原因。
  在来说说ArcGIS Runtime for Windows Mobile(原来的ArcGIS Mobile)这个产品。之所以将它也归为ArcGIS Runtime SDK,是不无道理的。使用过ArcGIS Mobile的朋友一定知道,它的一个核心理念就是将数据划分为Basemap Layer和Operational Layer。前者是指起可视化参考作用的数据,一般是栅格底图或地图服务切片;后者是包含矢量信息在内的业务数据。ArcGIS Runtime中所涉及到Package,恰好与之对应。Tile Package对应Basemap Layer,Map Package对应Operational Layer。可以说,ArcGIS Mobile的设计理念,在整个ArcGIS Runtime产品家族中,得到了很好的延续。其实ArcGIS Mobile是个十分优秀的产品……
  到此,新的三个移动SDK(iOS/Windows Phone/Android)也归为ArcGIS Runtime SDKs的原因也就明了了,主要原因是都基于REST API的框架。另外基本可以肯定,它们都会使用相同的数据模型,即各种Package。由此不难推断,以后iOS/Windows Phone/Android平台上移动SDK的离线功能,也会依赖于Tile Package和Map Package。上周刚刚发布的ArcGIS API for iOS 2.1(以后要叫ArcGIS Runtime SDK for iOS)版本中,已经印证了这种猜测——加入了对Tile Package的支持,实现了原生的底图数据离线功能。其它两个移动平台的离线功能,敬请期待。
  由iPad这个产品引发的变革已经展开,移动设备数量正在爆炸性的增长,桌面设备和移动设备之间的概念会越来越模糊。Windows 8的发布预示着微软已经做出了改变,它即可以运行在桌面电脑上,也可以运行在平板设备中。有人说Windows 8的意义对于微软不亚于当年的Windows 95,我同意这个观点。Esri也做出了积极的改变,新的产品体系,新的产品命名,新的ArcGIS Runtime。

在ArcGIS Web API应用程序中使用灰度地图

  上个月底,ArcGIS Online发布了一款全新风格的底图服务Light Gray Basemap。该底图服务尽可能少地使用了色彩,标注和要素内容,目的是突出地图的主题内容,把重点展示给最终用户。美化的底图固然好看,但有时我们不能为了使用地图而去使用地图,忽略了我们真正想要表达的意思,很多时候,都会回归到以简为美的原点。使用这种底图服务,我们可以很轻松的将注意力集中在业务数据上。
AGOL_LE_2_basemap_types

  可以看到,只有在右上角的Light Gray Canvas底图上,四个要素点才清晰可见。
  其实Google Maps API中就一直能够设置显示风格,以适应我们的需求。虽然ArcGIS Online已经推出了灰度底图服务,但对于自己的底图服务(尤其是国内数据用户)来说,如何能够风格化显示呢?以ArcGIS API for Silverlight为例来说明。
  缓存地图服务所对应的ArcGISTiledMapServiceLayer类中,暴露出了TileLoaded事件(继承自TiledLayer)。这个事件会在每一个切片加载完成时触发,并且事件参数中可以获得ImageSource属性,它就是切片本身,随后地图控件会对这些切片进行拼接,从而形成完整的地图。我们的工作,就是在这个事件中,对切片上的每个像素做色彩处理,从而达到风格化地图服务的效果。在Silverlight中,我们可以利用WritableBitmap来完成这项工作,代码如下,具体原理可自己参详:

   1: private void ArcGISTiledMapServiceLayer_TileLoaded(object sender, TiledLayer.TileLoadEventArgs e)

   2:         {

   3:             WriteableBitmap wb = new WriteableBitmap(e.ImageSource as BitmapSource);

   4:             for (int y = 0; y < wb.PixelHeight; y++)

   5:             {

   6:                 for (int x = 0; x < wb.PixelWidth; x++)

   7:                 {

   8:                     int pixel = wb.Pixels[y * wb.PixelWidth + x];

   9:                     byte[] dd = BitConverter.GetBytes(pixel);

  10:                     double R = dd[2];

  11:                     double G = dd[1];

  12:                     double B = dd[0];

  13:                     byte gray = (byte)(0.333 * R + 0.333 * G + 0.333 * B);

  14:                     dd[0] = dd[1] = dd[2] = gray;

  15:  

  16:                     wb.Pixels[y * wb.PixelWidth + x] = BitConverter.ToInt32(dd, 0);

  17:                 }

  18:             }

  19:             e.ImageSource = wb;

  20:         }

  下图是加载ArcGIS Online上StreetMap时的效果:
image  细心的朋友可能会发现,这个Silverlight程序并没有运行在浏览器中。这的确是一个OOB的程序,因为TileLoaded事件要求Silverlight程序必须获得提升权限才行。但是WPF和Windows Phone程序可以直接使用上述代码。
  那么在Silverlight应用中还能否显示风格化的地图服务呢?答案是肯定的。Map控件有一个Effect属性(继承自UIElement),系统提供了 BlurEffect,DropShadowEffect两个现成的效果。我们可以通过HLSL(High Level Shading Language)语言来自定义一些效果,比如灰度,通过PixelShader类应用到UIElement上,从而达到显示灰度地图的目的(此时Map控件中的所有图层都将变为灰色)。有兴趣的朋友可以动手实践一下,Windows Presentation Foundation Pixel Shader Effects Library这个项目中可以找到很多现成的效果。
  再来看看ArcGIS API for Javascript/Flex,两个API甚至都没有类似的TileLoaded事件可用。即使有,在Javascript中处理图片的颜色也是一项有难度的工作。不过不用担心,Portable Basemap Server从1.0.6版本开始,提供了风格化地图服务的功能,有灰度图和反色图两个选项,无需任何代码,即可在所有的ArcGIS REST客户端程序中,使用风格化的地图了:)
Untitled-1