`
tsv71tsv
  • 浏览: 21494 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

用JavaFX访问ArcGIS Server的REST服务(2)

阅读更多

用JavaFX访问ArcGIS Server的REST服务(2)
2010年12月22日
  上一篇文章介绍了ArcGIS Server地图服务JSON格式的元数据内容,下面介绍一下怎么用这些JSON数据构造出ArcGISTiledMapServiceLayer(类名与ArcGIS for JavaScript API保持一致)。
  这个类有一个成员变量url,在init{}代码块发送一个请求,请求的地址是地图服务的rest地址加上"?f=json"参数: init{ def http:HttpRequest = HttpRequest{ method:HttpRequest.GET; location:"{url}?f=json"; onInput:function(is:InputStream){ jsonHandler.parse(is); is.close(); loaded = true; } } http.start(); }      这段代码很好理解,构造一个HTTP的GET请求,然后通过start()方法发送这个请求。收到响应以后调用onInput函数,响应的内容就是地图服务的JSON格式的元数据。
  值得一提的是"jsonHandler.parse(is); "这行代码。由于JavaFX 中全是对象,所以没办法用Flex或JavaScript中的[]和.符号来访问JSON结构的数据。JavaFX开发包提供给我们的是javafx.data.pull.PullParser对象,随便google一下,就可以找到一些JavaFX解析JSON的例子代码,比如: def parser = PullParser { documentType: PullParser.JSON; input: is; onEvent: parseEventCallback; }; parser.parse(); is.close(); def parseEventCallback = function(event: Event) { if (event.type == PullParser.START_VALUE) { if (event.name == "Rating" and event.level == 2) { result.rating = Rating {} } else if (event.name == "Category" and event.level == 3) { category = Category {} } } else if (event.type == PullParser.END_VALUE) { if (event.name == "id" and event.level == 2) { result.id = event.text; } else if (event.name == "Title" and event.level == 2) { result.title = event.text; } //…更多else if } } 所有的解析工作都在onEvent事件处理函数里完成。看完这个例子让人有点绝望,这种"直白"的解析方式处理一层的结构或者是没有同名变量的JSON是没有问题的,一旦JSON格式比较复杂,比如层次解析起来就会有极大的麻烦。的JSON,spatialReference对象出现了多次,用这种if/else 秉承不行就换的原则,推荐使用一个开源工具--JFXtras(http://code.google.com/p/jfxtras),最新版本是0.7rc2,建议下载JFXtras-0.6.zip版本,可以学习一下其中的源码。
  def jsonHandler:JSONHandler = JSONHandler{ rootClass:"com.wj.ags.data.ServiceResponseAdapter" ; onDone:function(obj,isSequence){ _adapter = obj as ServiceResponseAdapter; } };      这个jsonHandler就是文章开始的那段代码中用来解析JSON的工具。我们只需要指定一个rootClass,解析过程中会自动把JSON中的值赋给rootClass的成员变量,在回调函数onDone中将得到一个rootClass的实例。这样我们的工作就简单多了,只需要按照JSON的结构声明若干类,比如我定义了一个com.wj.ags.data.ServiceResponseAdapter类来保存地图服务的描述信息(一件很happy的事情就是在rootClass中还可以嵌套使用自定义类,这样不管JSON有多少层,数据多么复杂,也能顺利解析完): public class ServiceResponseAdapter{ public var serviceDescription:String; public var description:String; public var mapName:String; public var copyrightText:String; public var layers:LayerInfo[]; public var spatialReference:SpatialReference; public var singleFusedMapCache:Boolean; public var tileInfo:TileInfo; public var initialExtent:Extent; public var fullExtent:Extent; public var units:String; public var supportedImageFormatTypes:String; public var capabilities:String; }  
  
  这是一个描述切片的经典示意图,我们实现在客户端访问切片的地图服务,最核心的一个功能就是根据地图显示范围计算切片的级别、行列号范围等信息。通过解析JSON已经构造出TileInfo的一个实例,它包含以下信息: public class TileInfo { public var dpi:Integer; public var format:String; public var compressionQuality:Integer; public var cols:Integer; public var rows:Integer; public var lods:LOD[]; public var origin:MapPoint; public var spatialReference:SpatialReference; LOD[]数组描述的是每个级别的切片信息,它的类结构如下: public class LOD extends Comparable{ public var level:Integer; public var resolution:Number; public var scale:Number; public var startCol:Integer; public var endCol:Integer; public var startRow:Integer; public var endRow:Integer;      每个LOD中都有startCol,endCol,startRow,endRow信息,这是一个控制信息,在计算切片的行列号时可以约束在这个范围内。这样可以避免请求空白的区域,减少客户端请求的数量是性能优化的一个重要方面!它们的计算方法如下:
  lod.startCol = Math.round((fullExt.xmin - origin.x)/lod.resolution/tileWidth - 0.5); 
  lod.endCol = Math.round((fullExt.xmax - origin.x)/lod.resolution/tileWidth - 0.5) - 1 
  lod.startRow = Math.round((origin.y - fullExt.ymax)/lod.resolution/tileWidth - 0.5); 
  lod.endRow = Math.round((origin.y - fullExt.ymin)/lod.resolution/tileWidth - 0.5);
  切片的行列号从原点开始计算,向右为X正方向,向下为Y正方向;而地图的extent的Y轴正方向是向上的。所以startRow的计算方法和startCol略微有些差别。奇怪的是我计算出来的每个级别的endCol总是比Service Directory里显示大1,所以人为地把这个值减去1,使其与Service Directory的描述一致。
  最后介绍ArcGISTiledMapServiceLayer最核心的两个个方法:1、根据地图当前显示范围计算切片的级别和起止行列号;2、根据级别和起止行列号获取图片。
  在计算切片级别和行列号的时候,地图窗口的大小是固定的,而用户指定的地图的extent值是"不准确"的。切片服务显示的本质就是按照行列号把图片排列在屏幕上,然后用地图窗口去截一块显示给用户。因此,地图的extent是和地图窗口大小绑定的。
  //根据lods中的比例尺级别选择一个适合显示当前范围的级别,前提是lods已经按照scale排序  var _min:Number = -1; for(_lod in lods){ var _difference:Number = 0; if(ext.width>ext.height){ _difference= Math.abs(ext.height - (this.height*_lod.resolution)); }else{ _difference= Math.abs(ext.width - (ext.width*_lod.resolution)); } if(_min地图显示的extent。接下来要计算该级别下的起止行列号。 var _startCol:Integer = Math.round((map.extent.xmin - tileInfo.origin.x)/tileInfo.lods[_chooseLevel].res olution/tileInfo.cols - 0.5); if(_startCol  tileInfo.lods[_chooseLevel].endCol) _endCol = tileInfo.lods[_chooseLevel].endCol; if(_endCol  tileInfo.lods[_chooseLevel].endRow) _endRow = tileInfo.lods[_chooseLevel].endRow; if(_endRow < 0)_endRow = 0; 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics