Zade's Weblog

程序人生

Monthly Archives: 7月 2009

After reading <>

  1. most GIS commercial firms have denied or ignored the cartographic generalization issue
  2. Generalization is not prerequisite vs Generalization is a usefull tool but the automated generalization is either a “NP-complete” probelm or the practical and ecnomic benefits of a solution are dubious.
  3. The generalization of digit products can no longer be driven by paper map production, as the needs for spatial data have become much broader and complex.
  4. Finally, the smaller the scale, the shorter the update cycle.Hence, if a NMA wants to maintain only one scale version then it has to update it frequently, with higher geometric accuracy,in order to respond to the update needs for all other smaller scael versions. This is a dilemma faced by NMAs which goes against the idea of one single database(the expression ‘scaleless or scale-free database’ , which lead to confusion, is purposefully avoided here; it seems that in case of data comming from surveys or photogrammetry, it would be more approriate to speak of pression, accuracy and resolution, not scale, since the notion of scale is meaningless in the absence of a mapping relation).

GIS数据采集-上海数据采集实例

我现在定制GIS的编程接口和实现, 是基于GeoAPI和我自己的编程经验, 但是我迫切的需要深入的了解实际中GIS数据采集和处理的流程和方法.

我在网上搜集了一些相关的论文, 整理一些资料, 这是其中的一个实例.

{Reference Type}: Journal Article
{Title}: 基于数据库的大比例尺基础地理数据缩编需要注意的一些问题
{Translated Title}: Problems of generalize Lager-scale Basic Geographic Data based on Database
{Author}: 陈四平
{Author}: Chen Siping
{Author Address}: 上海市测绘院,上海,200063
{Author Address}: 上海市测绘院,上海,200063
{Journal}: 现代测绘
{Translated Journal}: MODERN SURVEYING AND MAPPING
{Year}: 2007
{Volume}: 30
{Issue}: 5
{Keywords}: 数据设计
{Keywords}: 自动化处理
{Keywords}: 人机交互辅助缩编
{Keywords}: 地图回放
{Keywords}: 质量控制
{Abstract}: 提出了基于数据库的大比例尺基础地理数据缩编需要注意的一些问题,并从加强数据设计的完整性、作好数据准备工作,提高自动化处理功能,人机交互辅助缩编,满足地图回放的要求,加强数据质量控制等方面阐述了实现基于数据库缩编的实用方法.
{URL}: http://d.g.wanfangdata.com.cn/Periodical_xdch200705015.aspx
{Database Provider}: 北京万方数据股份有限公司
{Language}: chi

主要的体会有几点:

  1. 大比例尺的数据可以整理生成小比例尺的数据, 这个过程称之为缩编;
  2. 数据采集国家制定了相关的标准, 但是这个过程很慢, 标准的权威性也不够, 各个数据采集单位一般都会在这个基础上扩充. 这个过程和国际标准正好相反,国际标准一般非常的丰富,而各个应用单位在标准的基础上定制, 生成profile;
  3. 缩编的过程尽量自动化, 这个过程包括取舍,合并和移位
  4. 取舍:
    1. 直接根据属性字段直接取舍,例如"例如1:500缩编1:2000数据时直接删除各类地下检修井、管道附属设施等1:2000数据不需要表达的要素数据表项。"(参见Page1)
    2. 主要根据要素的空间属性进行取舍, 例如要素的面积和长度等
  5. 合并: "合并处理中,把一些不需要区分的要素内容直接进行数据表项的合并,例如1:2000缩编1:10000时栅栏、铁丝网和篱笆的合并。把同类性质、同属性相邻的空间数据进行合并,主要有植被、鱼塘、同层次同结构间距很小的建筑物等。"(参见Page1-2)
  6. 移位: 为了保持要素的空间属性, 例如相切等, 需要对要素移位

C++0x不支持concept

在2009年7月份德国法兰克福举行的C++标准委员会上, 经过投票公决, C++0x抛弃了无数C++Fans和C++大牛们期待已久的Concept特性; 要知道, 这可是C++ template的新式武器.

此消息一出, 不单吾辈C++爱好者颇感失望,连很多为之工作很多年的C++鼻祖式人物也不免叹息.参见Bjarne Stroustrup对此的解释

简单的说, Concept被剔除的原因是很多的委员觉得Concept不够成熟,担心成为类似于异常规范和template export属性一样的鸡肋式特性. 不够成熟所以不被采纳, 但是只有被采纳以后才会更加的成熟, C++标准委员会就陷入了这样的泥潭, 难以自拔.

在google视频, informit和devx上, 我已经见了很多关于Concept的介绍和具体的使用方式, 觉得至少在使用方式上会给我们很大的方便. 现在这个特性没有了, 我从一个使用者的角度总结了一下Concept带给我们的便利:

  1. 错误消息
    这几乎是Concept带给我的最初的便利
  2. 基于Concept的重载
    极大的减少typename的使用频率,和静态函数重载颇为类似
  3. 很多诡异的template trick就不必要了
    没有Concept, 我们需要探索很多的隐晦的C++ template trick

除掉这些, 似乎也没有更多的.

Bjarne Stroustrup虽然失望, 但是不认为这是一个灾难; Hurb Sutter则更加轻松, 也许是因为他没有参与concept的设计.

After Reading preface of <>

  1. Often, a problem is only fully understood through the process of programming a solution for it
    分析->设计->代码, 这是软件开发的一般流程. 但是只有当你实现了所有的设计, 你才算把设计搞好, 需求弄清楚. 否则, 设计存在小洞, 分析还有模糊的地方, 只有实现设计的源代码才是把问题彻底弄清楚的唯一途径.
  2. Why would you want to program? Our civilization runs on software.
    电子化, 数字化,全球化… 这一切的基础表现为运行在硬件之上的软件. programmer们, 让我们为之骄傲吧!
  3. Code can be beautiful as well as useful
    兼顾实用和美观, 技术和艺术, 这是任何一个领域的最高准则.

渲染要素的淘汰策略

我们前面提到了我们实现要素显示淘汰策略的实现(针对点要素和注记要素):按照重要性排序,渐次的基于冲突策略铺开符号和注记,直到遍历完所有的要素.

对于一个特定的图层来说, 他可能对应于几个比例尺, 这意味着在每个比例尺下都要进行这样的淘汰策略. 如果只是简单的按照这样的策略进行, 算法实现也会很简单, 但是现实世界并不是如此的美好.

我们发图人员的一个需求是:在大比例尺下出现的数据, 在小比例尺下必须出现. 例如1:10000比例尺下出现的点, 在1:5000的时候一定要有. 这符合一般用户的心理期望.问题是我们的算法满足这个期望吗?

不一定!

我们举一个反例: 有三个点按照重要性排序是:A,B,C,在1:10000比例尺下,按照算法,先放A;放B冲突,舍弃;放C可以. 在1:5000比例尺下,先放A; 放B不冲突;因为放B导致放C冲突,舍弃.这样1:10000比例尺下放的AC,1:5000比例尺放的是AB,1:10000下的要素B没有子在1:5000下出现.这好像并不满足用户的要求.

究竟是我们的算法合适,还是用户的需求合适,我们让二者PK一下.

算法:出现AB是因为B的重要性要大于C,C没有出现因为他的重要性低; 而重要性不正是用户的需求吗?

用户需求: 大比例尺的要素一定要出现在小比例尺的地图上, 因为比例尺越小, 地图越详细,不应该出现大比例尺下出现的要素,小比例尺下不出现,这会严重的影响用户的体验.

我们这里列举的只是理论上的冲突,实际上二者可能非常的一致. 例如在1:5000比例尺下,ABC都出现了,而这正是用户想要的.我们上面举的反例只是在一种特殊的情况下才会出现;而且出现了并不一定不合理.

我们可以定义一种策略, 基于重要性最优的, 基于大比例尺地图要素一定要被包含到小比例尺地图中.这两种策略都反映了用户的需求.

南戴河游记

为了促进百星之间的交流和互动,人事处的王晓虹老师和吕晓洁组织了这次百星南戴河活动。刚开始的时候,我还有些忐忑,因为百星成员之间虽然有所交流,但是大家之间并不熟悉,相处起来会不会有所尴尬呀。结果表明这完全是杞人忧天,相反的是我们不仅达到了破冰的目的,还玩得非常的尽兴。

我们约好是周六早上9点出发,大家都比较准时。迎着早上明媚的阳光,我们向着秦皇岛南戴河进发了。

在车上我和包云岗坐在一起,这无非是因为我们的百星成长导师同为龙芯总设计师胡伟武。在路上,我和包云岗就天南海北的聊了起来,从路边的小河到他老家的太湖,从他老家的水稻到我老家的小麦,从路上带有变形金刚图案的运输车到龙芯买断MIPS指令系统,从我实现文本光圈效果到他最近研究的内容”制约计算机性能最优的因素”……,这真是痛快。虽然汽车行走了将近7个小时,我也并没有觉得时间过得很慢。

其实,一般4-5个小时就应该到南戴河的;之所以拖到近7个小时,是修路堵车导致的。还好吕晓洁,刘洁等这些女孩子细心,带了一些开心果之类的零食。她们大方的广为布施,我们也不客气,才不至于饿的头昏眼花。

等我们在目的地下车的时候,已经是将近下午4点了。我们下榻的是南戴河国际娱乐中心,类似于一个大庄园,里面小桥流水,花红柳绿,确实让人心旷神怡,一下子感觉也不是那么饿了。话虽如此,饭菜一上,大家几乎一扫而光。人是铁,饭是钢,这是硬道理。

饭后是自由活动时间,我们简单的在房间整理了一下行李,就向海边出发了。到了南戴河,当然应该看看大海。我本来还准备了泳裤和泳镜,准备在海滩畅游一番,但是被导游和王老师制止了,理由是现在天气还比较凉,并且也不太安全。我虽然可以自信我的安全,也不在意天气,但是集体活动要听统一安排,也只能服从了。

我,包云岗,莫志锋等几个人在一起,很快就到了海边。我们顺着一条通到海里的高架桥,到了几条渔船汇集的小港口。蔚蓝的大海苍茫无边,和同样蔚蓝的天空融为一体,夹杂着海水味道的凉风迎面吹来,那叫一个爽。那句话怎么说来着:在一个伸手不见黑夜的五指,小风嗖嗖的吹着……

我们也趁机合影留念。莫志锋的相机真好,分辨率是4752×3168,够大的。

海边有人玩球,拔河;海里有人游泳,打水仗,真热闹。我们虽然不能游泳,至少可以手提拖鞋,在海边趟水,即使短裤被海水打湿也在所不惜。

不过剩下的时间毕竟不多,我们还约好6点在庄园的小亭集合,玩杀人游戏,然后7点多吃饭。所以差不多在5点半的时候,我们陆续地就开始往回走了。

紧挨着海边的是庄园的滑沙场地,一个巨大的梯面型的沙丘,和地面大约呈35度角。从上面搭乘一个滑板滑下来,非常的刺激。前几年我到南戴河的时候,曾经玩过这个游戏。现在已经是下班时间,所以也没有值班人员。这个时候包云岗做了一个惊人的举动,他把手中的水瓶和胶鞋往地上一丢,撒开脚丫顺着沙丘向上爬。沙丘的垂直高度至少有30米,他几乎是一路小跑上去的,看得我和王晓虹老师目瞪口呆。

沙丘的下面有几个滑板,可能是工作人员的疏漏,没有收集回去。我,熊刚和骆卫华各自提了一个滑板,准备沿着傍边的走梯爬上去,然后玩一次滑沙游戏。谁让工作人员的疏漏成全了我们呢。吕晓洁这个丫头竟然也空着双手和我们一起爬梯,难道她想让我们表现绅士风度,把滑板让给她玩一次?

滑板不是太沉,但是提上去也够累人的,快到顶端的时候我们已经气喘吁吁。走在最前面的熊刚回过头来,一脸委屈的对我们说:”兄弟们,上面有大量闲置的滑板,可以随便用。”我苦笑之余几乎晕倒,我们费九牛二虎之力提来的滑板,算是给娱乐中心做无偿贡献了。

他们几个好像没有玩过这个游戏,没有工作人员的辅助,所以有些担心。我几年前玩过这个游戏,所以我准备第一个滑下去。他们在后面一松滑竿,我坐在滑板上面顺着坡面就滑了下来。也许是因为沙子有些潮湿,滑的不是很顺畅,滑板半横着,在半山腰被卡住了。我通过双脚摆正方向,晃晃悠悠的滑了下来。

吕晓洁是第二个。或许是因为我趟顺了路,她一泻千里的就滑了下来,我们都为她鼓掌喝彩。可她嫩马失蹄,晚节不保,在最后的时候从滑板上跌了出来,弄得满脸泥沙,一只拖鞋还像炮弹似地的射了出来。我们笑着把她搀扶起来,还好没事。不过骆卫华比她还惨,在半山腰就从滑板上跌了出来,打了几个滚才下来。

大家也是在这个时候聚齐了,我们就一起来到庄园西面的一个小亭子里面,准备玩杀人游戏。当时正值黄昏,夕阳微照,凉风习习,亭台楼阁,颇有诗情画意的味道。在这样的环境下一起玩游戏,真是再好不过了。

我们前面提到,这次游玩的一个主要目的是破冰,就是让大家熟悉起来,而杀人游戏就是王老师和吕晓洁她们精心准备的一个节目。我必须说,这个游戏太适合这个目的了。算上这次下午我们在小亭子里,晚上在客厅里,还有第二天上午在另外的一个小亭子里,我们一共玩了3次这个游戏。除了破冰的目的,我们确实是爱上这个游戏了。

杀人游戏虽然听起来很恐怖,但其实就像一个舞台,你通过推理,辩解,陈述,甚至起哄等方式表演自己。在这个过程中别人认识了你,你也认识了别人。我通过几个具体的场景说明我是怎么通过这个游戏熟悉我们当中一些人的。当然,你要熟悉这个游戏的规则和术语,例如平民,警察和凶手等。如果像我一样第一次玩这个游戏,那么先google一下吧。

场景一:刘洁是我们这次百星活动少有的一个女性成员,在游戏过程中,她被大家一致认定为凶手。按照规则,她必须香消玉殒,不得发言,眼睁睁的看我们玩下面的游戏,当然她可以发表”临终遗言”。她一脸委屈的简短总结是:”我也没什么好说的”。一般来说,被认定的凶手临终都会替自己辩解,含冤致死的会称自己是平民或者警察;当然真正的凶手也会这么做,目的是混淆视听,搅局,保护同伴。像刘洁这样的简短的”临终遗言”并不多见,这几乎就直接的暴露了她的凶手身份。不过通过这个环节发现:我们可爱的刘洁同志可真是一个老实孩子。

场景二:有一次张戈被认定为凶手,在大家公决以前,他可以为自己辩解。张戈满脸义愤的开头语是:”我发誓我不是凶手”。此语一出,满座皆惊。虽然他成功的逃脱了这次的凶手指责,但是他的警察身份也暴露了,所以很快在下一轮的循环中被凶手杀死。我们看到了张戈同志刚烈的性格,在蒙冤时候强大的反抗力量。

场景三:钟石强在游戏过程中,不经意的说了一些类似于”女人一般比较啰嗦”的言语,当然这立刻招致女同胞的反击,他也几乎被砖拍死。不过这也说明了钟石强快人快语,胸无遮拦的性格;和这样的人交朋友,你一点也不用担心。

场景四:叶剑的声音低沉洪亮,充满了男性磁性的魅力,几乎可以当新闻联播的播音员了。有一次在第一轮循环过程中叶剑就被杀死,他用标志性嗓音作“临终遗言”:“其实我是警察,我已经指认了一个平民,就是赵红超。我就说这些”。本来这样的说法也可能被怀疑为凶手,但是几乎全部的平民都相信了他的话。我想除了我们这些人本身比较老实本分以外,叶剑的嗓音也起了很大的作用。不过最得意的人是我,因为在后面的游戏中,平民不会冤死我,凶手不会杀死我,逍遥自在直到最后。感谢叶剑,感谢他的嗓音!

场景五:韩银和在这个活动中非常的活跃,他充满夸张和理性的表现,让人不得不相信他的话,他的辩解几乎每次都能成功。有一次他在王晓虹老师身边,和他往常的平民身份的表现一样,甚至还非常乖巧的向王老师表示:”你杀谁,我杀谁”,一副典型的”跟风”做派。谁能料到,他就是隐藏最深的凶手,搞得王老师周围的女同胞对他的老谋深算喟叹不已。

场景六:王伟平在一次游戏中被认定为凶手,”临终遗言”的时候他非常平静的说:”其实我是警察”,然后指定谁是被他指认的平民。我们剩下的平民就按照他的指认继续这个游戏,最终在我的印象中这是一次唯一的凶手胜利的结局,而王伟平就是真正的凶手。王伟平凭借这次的表现获誉”金牌杀手”,王老师也不停的赞叹:游戏就该这么玩。

场景七:我记得这是周六晚上的最后一轮游戏,我非常幸运的扮演了警察的角色。由于入道不久,道行还浅,我不免有些紧张。不料这竟然招致包括范文宇老师在内的一些平民对我是凶手的怀疑,几乎把我塑造成当世窦娥。最郁闷的是这次杀死我的不是凶手,而是平民误杀。当时吕晓洁是真正的凶手,为了掩人耳目,她莫须有的以概率论为理论基础,指认韩银和为凶手。当时指认我是凶手的有三票,指认韩银和的有两票。虽然我作为警察已经确认了小韩同志的平民身份,但是我也只能跟风指认他;否则如果我被冤死,那么游戏结束,凶手就胜利了。所以加上我的一票,韩银和也得了三票。关键时刻刘洁同志站了出来,也指认韩银和为凶手,我幸运的保住了性命。最后迂回曲折,吕晓洁和包云岗的凶手身份都被指认出来,我作为警察取得了最后的胜利。阿门!感谢刘洁的误打误撞,感谢吕晓洁的概率论,向韩银和同志致以最崇高的歉意。

这样的场景还有很多,有的我已经记不得了。大家玩的都很尽兴,也达到了破冰的目的。直到晚上8点我们才结束,回到餐厅吃饭。9点的时候大家还意犹未尽,继续玩杀人游戏,直到晚上12点。

我和包云岗住一个房间,我们俩洗漱以后仍然没有睡意。他问我工程中遇到的主要问题,我向他请教direct i/o理论,我们两个在工程和研究的相互关系问题上几乎讨论到晚上2,3点,才迷迷糊糊的睡去。

第二天的早饭是在早上7点左右。虽然我和包云岗睡的很晚,但是早上不到6点就醒了,这可真是精神的力量。

早饭后我们取消了滑沙和滑草等活动,在海边租借了一个足球,玩起了沙滩足球游戏。简单的分了一下组,我们就开始了。虽然太阳火热,但是大家玩的也很卖力。我玩了几个加速跑就有些头昏眼花,再加上火热的太阳,躺在沙滩上休息了好长的一段时间我才缓过来。看来,得加强锻炼呀。

沙滩足球以后,大家又玩了一会沙滩排球。不过太阳越来越热,我们大约在10点左右就结束了这个游戏,返回路边的一个小亭子里继续我们的杀人游戏。看来我们确实是喜欢上了这个游戏。

直到午饭的时间,大家才恋恋不舍的结束了杀人游戏。吃饭的时候,大家还七嘴八舌的讨论刚才的杀人游戏中的有趣片段,这种讨论一致延续到我们登上回家的客车。

这次回来没有了堵车现象,所以相对比较顺畅,用了不到5个小时就回来了。不过大家已经很累了,在路上大部分都睡了一觉。

快到北京的时候,大家又兴奋起来,纷纷表示这次游玩的收获很大,尤其是杀人游戏。王老师还开玩笑的说,我们以后制定一个杀人游戏的评段制度,高级玩家可以通过类似于围棋一样的段位来评价。我们笑着一致认同。

在后面的通讯交流中,我们一致的表达了这样一个想法:期待下一次这样的活动。就用它作为结束语吧。

异常处理的一个实例

因为异常处理符合中立原则, 还有许多的其他好处, 因而在实践中大行其道; 使用错误返回值的处理错误的方式已经不多见了.

我在实践中走了一条这样的路: 开始的时候使用异常处理错误;因为某些原因改回返回值的方式;现在又想使用异常的方式. 这样的一个思想的过程, 说明了处理错误的复杂性,以及异常和返回值方式的优缺点.

我觉得这个过程很有意思, 能够说明很多的问题, 现记录下来, 以备后用.

开始我遇到的问题是通过URI加载图层,当加载失败的时候抛出异常. 在这里我们把加载数据失败定义为一个错误, 使用异常的方式处理它.

然后继续我们的问题: 很多图层合并为为一个图层集合, 成为我们工程文件的一部分. 为了支持持久化, 我们通过文本文件的方式存储工程属性,这样用户就可以在以后的某个时间打开工程文件,继续以前的工作.

我们使用伪码说明我们的具体实现:

void load_project(string file_path){

try{

   parse_project_file(file_path);

}catch{exception e}{

      MsgBox(e.what());

}

}

void parse_project_file(string file_path){

   for_each(read layer_uri from file_path){

     load_layer_from_uri(layer_uri);

  }

}

layer load_layer_from_uri(string uri){

  if(bad_uri(uri)){

     throw BadUriException();

  }

  return real_load_layer(uri);

}

我们看到, 在中间代码函数load_layer_from_uri中抛出异常, 在中间代码函数parse_project_file中对异常中立, 在最终代码函数load_project中处理异常.这就是我们日常的代码生活.

假定我们有3个layer_uri,其中第一个发生了错误(例如文件名被意外的修改了), 那么后面两个的layer也不能加载了(即便他们是正确的), 最终用户得到的是一个异常消息框.

这样做是用户希望的吗? 也许是, 也许不是. 例如在ArcGIS中, 如果发生上面的错误, 那么用户见到的是两个正确加载的图层, 一个发生错误, 没有正确加载的图层, 并且用户可以在以后某个时间修正这个错误图层的属性(例如把文件名修改回来),然后再正确的加载这个图层.这样处理错误的方式也许是用户更喜欢的.

假如我们采用和ArcGIS相同的方式,我们的代码会有什么影响? 应该是以下的样子:

void load_project(string file_path){

parse_project_file(file_path);

for_each(layer in layer_set){

   if(layer.is_loaded()){

      layer.display_success();

  }else{

    layer.dispaly_failed();

  }

}

}

void parse_project_file(string file_path){

   for_each(read layer_uri from file_path){

     load_layer_from_uri(layer_uri);

  }

}

layer load_layer_from_uri(string uri){

  if(bad_uri(uri)){

     return layer.with_failed_flag();

  }

  return real_load_layer(uri);

}

我们看到, 不同的用户需求还是很大程度上影响了我们的代码组织和实现.

第一种方式简单, 符合我们日常编码的经验; 第二种方式复杂, 但是也许是最终用户希望的.

假如我们使用第一种方式, 那么用户如何修改错误呢? 可以通过工具修复工程文件, 也可以直接的修改工程文件.

我现在比较认可第一种方式.

中立原则-函数异常和函数返回值

传统的编程语言处理错误的方式是使用函数返回值, windows API有大量这样的实例. 在OO兴起以后, 异常处理成了处理错误的经典方式.

异常处理有很多的好处, 例如异常不能被忽略, 异常传递自动进行, 异常使得代码书写非常简洁等.

有的时候我们还是使用函数返回值, 例如查找函数find,如果没有找到则返回-1, 而不是使用异常表示没有找到. 当然我们也可以非常哲学的说, 查找失败并不是一个错误, 所以不会使用异常.这样的逻辑也即意味着:在一般情况下,错误要使用异常处理的方式. 这可以定义一个我们日常编程的一个原则.

这个原则当然是对的; 这是因为我们前面提到的异常处理错误的各种益处. 但是我们这里要说的是, 异常处理还符合了编程的另外一个原则:中立原则. 我很少见到有人提到这个原则, 但是我认为这个原则太重要了.

为了说明这个问题, 我们把日常的代码分成两类: 中间代码和最终代码.

所谓中间代码即是被复用的代码, 我们常见的代码很多都是中间代码,例如所有的库代码都是中间代码, 这包括C++的STL,以及各种开源的代码库等.

所谓的最终代码即不被复用的代码, 例如我们代码中的处理各种用户消息的代码.

我们代码的90%几乎都是中间代码, 中间代码也生成了所谓的中间件. 具体到错误处理, 严格的说,中间代码并不知道如何处理错误. 这正是Bjarne Stroustrup指出的:知道错误发生的人(中间代码书写者)不知道怎么处理它, 知道怎么处理错误的人(最终代码书写者)不知道它什么时候发生.

中间代码不知道如何处理错误的原因是: 在不同的环境下, 人们看待错误的角度不同, 所以处理方式也不一样; 也就是说, 只能交给最终的代码去处理. 从中间代码的角度来看, 只能是对错误”视而不见”,保持沉默和中立.

异常处理的机制使得对错误保持中立非常的简单; 反过来说, 处理错误就麻烦一些(使用大量的try/catch).

异常处理仅是中立原则的一个体现, 还有很多的方式体现中立原则.

地图发布的二维视图

地图发布的过程,可以从两个维度审视,数据的维度和比例尺的维度.

比例尺的维度很明显, 任何纸板的地图都是在一定的比例尺下;当然, 矢量的数据也是必须的.

假定我们发布n级的比例尺,有m层的矢量数据. 对于特定的比例尺i, 发布的地图并不一定需要所有的m层数据;对于特定的图层j,也也并不一定发布在所有的比例尺上面.我们可以得到一个地图发布的二维视图:

image

根据我们现在的经验, 对于一个特定的图层而言, 一般发布1-3级的比例尺;一级比例尺一般需要的图层个数则没有特定的规律,这需要特定的需求而定.

我们定义的显示符号(Symbolizer)和比例尺关联,整个图层(FeatureLayer)也和比例尺关联,某些图层的集合(Layer)要和比例尺关联,这是比例尺在地图发布中作用的体现.

至于矢量数据,通过Layer关联.

矢量显示符号的缓存

我们这里所说的矢量显示符号的缓存, 不同于矢量要素的缓存;因为矢量要素的缓存还要考虑其label的缓存, 这是另外一个话题.

矢量符号的缓存一定是和比例尺关联在一起的,而比例尺的定义是出图规划的第一步.

点状要素比较特殊,因为在考虑缓存的时候, 不仅要考虑点状要素之间的间隔,还要考虑和点注记之间的避让;而线面要素一般没有这个问题.点状要素的这个特殊性我们在另外的一个话题讨论, 我们现在主要关注矢量符号的缓存.

在每个特定的比例尺下面, 我们可以根据一定的原则,让算法动态的控制要素的显示.显示的算法是要付出一定的代价的,我们可以按照某种方式缓存算法的结果,从而重复利用,不必每次都动态计算.

要素的显示都是和比例尺关联的, 例如我们定义3级比例尺,1:20000,1:10000,1:5000. 那么我们可以这么合理的假设:

  1. 在1:20000比例尺下显示的要素,在任何比例尺下都显示,即便是很多的要素叠加在一个很小的显示区域内.
  2. 在1:10000比例尺下显示的要素,一般包括1:20000下显示所有要素,还有一些增加的要素,这些增加的要素只有在比例尺分母<=15000的时候我们才显示;
  3. 在1:5000比例尺显示的要素,一般包括1:10000下显示所有要素,还有一些增加的要素,这些增加的要素只有在比例尺分母<=7500的时候我们才显示;

假如我们增加一个Double类型的字段"__MaxScaleDenominator",用于定义要素显示的比例尺分母的最大值.这样为了统一,我们定义1:20000下的最大值是+∞.

下面我们讨论这种做法的合理性.

对于在大比例尺下显示的要素,在小比例尺一般都显示,这是符合直觉的.我们这里问题的实质是:离散比例尺下要素的显示按照怎样的算法过渡到连续比例尺下,这是一个插值的算法.如图:

image

这里的插值对象包括两个概念, 一个是要素的显示与否需要插值,例如在1:20000下显示100个要素,在1:10000下显示200个要素,那么在1:15000下,我们显示多少个要素(按照线性插值应该是150个要素);另外一个是显示符号需要插值,例如在1:20000下显示点符号的大小是2毫米,在1:10000下显示符号的大小是4毫米,那么在1:15000下,我们显示符号的大小是多少(按照线性插值应该是3毫米).

线性插值和最邻近插值是最简单的两种插值方法,其中以最邻近插值更为简单. 对于我们的问题而言,线性插值仍然是过于复杂了,这不但需要我们做更多的运算,导致我们的缓存的目的大受影响. 因为我们目的是为了避免运算, 而线性插值仍然要求我们进行运算.

所以我们采用最邻近插值.

按照这个插值公式, 1:20000下的要素应该在所有的比例尺下都显示;1:10000下的所有要素在<15000下都显示;1:5000下的比例尺在<7500下都显示.

在这种情况下, 容易给人一种误解:认为在随机生成的显示符号上,所有的要素都显示;而不是像用户自定义的符号需要比例尺的控制. 例如我们的制图人员就曾发问:如果我们需要显示所有的要素怎么办?

其实这句话的真正含义是: 我想让要素在没有比例尺控制的状态下显示, 而不是通过比例尺控制其显示.

实际上, 没有所谓的不通过比例尺控制的图层显示. 如果把所有的要素都密密麻麻的显示在一个有限的范围内(例如100万个要素在显示器上显示出来),这不仅浪费时间, 而且没有必要. 一个聪明的程序总是通过一个特定的过滤器淘汰某些要素. 只不过这个淘汰策略是计算机内部实现的;而用户自定义的显示符号则是用户通过重要性等参数来控制要素的显示.

用户指定的策略显然要比计算机内部随机的算法要更加的合适.

从实现的角度来看, 我们按照用户指定的比例尺范围,在每个特定比例尺的内,生成要素的最大比例尺分母,把这个数值写进要素的属性中(例如"__MaxScaleDenominator",如果没有则创建这个字段).然后在随后的显示中,通过名称获得这个属性的数值,直接的和当前的比例尺比较,确定其是否显示.