数据是GIS的核心,没有数据一切无从谈起,Internet上有很多在线WMS地图服务资源,我们可以好好利用这些数据资源,比如天地图、必应地图、NASA、OGC数据服务等等。
在我们国家常用的还是天地图的地图服务资源,详见:http://blog.3snews.net/space.php?uid=6955280&do=blog&id=67981,这篇博客列举了一些常用的在线地图服务资源,读者可以自行试下。
1、添加天地图地图服务
由于上篇转载的平常心的博客对WMSTiledImageLayer已经讲的非常清楚了,这里不再赘述了,看代码:
public static WMSTiledImageLayer addTianDiTuImage() throws Exception { // 请求地图的URL String uri = "http://www.scgis.net.cn/imap/iMapServer/defaultRest/services/newtianditudom/WMS"; // WMS WMSCapabilities caps; URI serverURI; serverURI = new URI(uri); // 获得WMSCapabilities对象 caps = WMSCapabilities.retrieve(serverURI); // 解析WMSCapabilities数据 caps.parse(); // // 输出wms元数据信息 // System.out.println(caps.getCapabilityInformation().toString()); // 获取所有图层(这里只有一个,自己用geoserver发布的则可能有很多) final List<WMSLayerCapabilities> namedLayerCaps = caps.getNamedLayers(); String layerName = null; for (WMSLayerCapabilities wmsLayerCapabilities : namedLayerCaps) { layerName = wmsLayerCapabilities.getName(); } AVList params = new AVListImpl(); // 图层的名称 params.setValue(AVKey.LAYER_NAMES, layerName); // 地图服务的协议,这里是OGC:WMS params.setValue(AVKey.SERVICE_NAME, "OGC:WMS"); // 获得地图的uri,也就是上面定义的uri params.setValue(AVKey.GET_MAP_URL, uri); // 在本地缓存文件的名称 params.setValue(AVKey.DATA_CACHE_NAME, layerName); params.setValue(AVKey.TILE_URL_BUILDER, new WMSTiledImageLayer.URLBuilder(params)); WMSTiledImageLayer layer = new WMSTiledImageLayer(caps, params); return layer; }
这里添加天地图影像作为底图,效果还是不错的,看下效果图:
另外,天地图还提供了在线注记底图服务,只需改动上面的地图请求地址即可,看下效果图:
这里加载的效果要比天地图的在线三维球的效果要好,有兴趣的可以去对比下。另外天地图的在线三维体验还是比较差的,只是单纯的浏览,单是这个体验效果还不是很好,毕竟这是政府的东东,不像Google财大气粗,全球几十万台服务器,天地图的商业化发展之路任重道远啊。
2、添加Geoserver发布的地图服务
这里暂时只发布了世界国界和中国县界数据(面和线),这些数据稍后会打包发到我的CSDN资源,大家有需要的可以去下载。为了方便服务资源的管理,在右边添加了一个简单的WMS服务器管理面板,后面仿照ArcCatalog做一个WMS服务器管理的模块。
3、WMS服务器管理面板
examples中有个WMSLayerManager,这个demo实现了wms服务的基本管理,只需根据自己的需要修改源代码即可。这里说下我自己修改源代码的方法,首先将需要修改的java文件在改目录下copy一份,名字自取,这样做的好处是不破坏源代码的完整性,因为其他的demo也可能用到这个java文件,避免了大量的修改,改完只需将src导出jar包即可。如下图所示:
改动的不多,都加了注释,可以对比原WMSLayersPanel.java文件看下不同,修改后的SmartScopeWMSLayersPanel源码如下:
/* Copyright (C) 2001, 2006 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. */ package gov.nasa.worldwindx.examples; import gov.nasa.worldwind.*; import gov.nasa.worldwind.avlist.*; import gov.nasa.worldwind.globes.ElevationModel; import gov.nasa.worldwind.layers.*; import gov.nasa.worldwind.ogc.wms.*; import gov.nasa.worldwind.terrain.CompoundElevationModel; import gov.nasa.worldwind.util.WWUtil; import gov.nasa.worldwindx.examples.WMSLayersPanel.LayerInfo; import javax.swing.*; import javax.swing.border.*; import java.awt.*; import java.awt.event.*; import java.net.*; import java.util.*; import java.util.List; /** * * @项目名称:worldwind-1.5.0 * @类名称:SmartScopeWMSLayersPanel * @类描述: WMS服务图层管理面板 * @创建人:刘硕 * @创建时间:2015年2月4日 下午5:09:31 * @修改备注: * @版本: */ public class SmartScopeWMSLayersPanel extends JPanel { /** * @Fields serialVersionUID : TODO */ private static final long serialVersionUID = 1L; protected static class LayerInfo { protected WMSCapabilities caps; protected AVListImpl params = new AVListImpl(); protected String getTitle() { return params.getStringValue(AVKey.DISPLAY_NAME); } protected String getName() { return params.getStringValue(AVKey.LAYER_NAMES); } protected String getAbstract() { return params.getStringValue(AVKey.LAYER_ABSTRACT); } } // 所有图层元数据信息 protected String[] servers; protected List<WMSLayerCapabilities> namedLayerCaps; protected WorldWindow wwd; protected URI serverURI; protected Dimension size; protected Thread loadingThread; protected TreeSet<LayerInfo> layerInfos = new TreeSet<LayerInfo>( new Comparator<LayerInfo>() { public int compare(LayerInfo infoA, LayerInfo infoB) { String nameA = infoA.getName(); String nameB = infoB.getName(); return nameA.compareTo(nameB); } }); public SmartScopeWMSLayersPanel(String[] servers) { } /** * * 创建一个新的实例 SmartScopeWMSLayersPanel. * * @param wwd * @param server * @param size * @throws URISyntaxException */ public SmartScopeWMSLayersPanel(WorldWindow wwd, String[] server, Dimension size) throws URISyntaxException { super(new BorderLayout()); this.servers = server; this.wwd = wwd; this.size = size; this.setPreferredSize(this.size); this.makeProgressPanel(); // Thread off a retrieval of the server's capabilities document and // update of this panel. this.loadingThread = new Thread(new Runnable() { public void run() { load(); } }); this.loadingThread.setPriority(Thread.MIN_PRIORITY); this.loadingThread.start(); } /** * * @方法名称: load ; * @方法描述: 加载服务 ; * @参数 : * @返回类型: void ; * @创建人:刘硕; * @创建时间:2015年2月5日 上午9:33:23; * @throws */ protected void load() { WMSCapabilities caps = null; try { for (int i = 0; i < servers.length; i++) { this.serverURI = new URI(servers[i].trim()); caps = WMSCapabilities.retrieve(this.serverURI); caps.parse(); // 获取该服务下的所有图层元数据描述 namedLayerCaps = caps.getNamedLayers(); if (namedLayerCaps == null) return; try { for (WMSLayerCapabilities lc : namedLayerCaps) { Set<WMSLayerStyle> styles = lc.getStyles(); if (styles == null || styles.size() == 0) { LayerInfo layerInfo = createLayerInfo(caps, lc, null); SmartScopeWMSLayersPanel.this.layerInfos .add(layerInfo); } else { for (WMSLayerStyle style : styles) { LayerInfo layerInfo = createLayerInfo(caps, lc, style); SmartScopeWMSLayersPanel.this.layerInfos .add(layerInfo); } } } } catch (Exception e) { e.printStackTrace(); return; } // 在面板上显示所有图层的名称 EventQueue.invokeLater(new Runnable() { public void run() { SmartScopeWMSLayersPanel.this.removeAll(); makeLayerInfosPanel(layerInfos); } }); } } catch (Exception e) { e.printStackTrace(); } // Gather up all the named layers and make a world wind layer for each. } public String getServerDisplayString() { return this.serverURI.getHost(); } protected LayerInfo createLayerInfo(WMSCapabilities caps, WMSLayerCapabilities layerCaps, WMSLayerStyle style) { // Create the layer info specified by the layer's capabilities entry and // the selected style. LayerInfo linfo = new LayerInfo(); linfo.caps = caps; linfo.params = new AVListImpl(); linfo.params.setValue(AVKey.LAYER_NAMES, layerCaps.getName()); if (style != null) linfo.params.setValue(AVKey.STYLE_NAMES, style.getName()); String abs = layerCaps.getLayerAbstract(); if (!WWUtil.isEmpty(abs)) linfo.params.setValue(AVKey.LAYER_ABSTRACT, abs); linfo.params.setValue(AVKey.DISPLAY_NAME, makeTitle(caps, linfo)); return linfo; } protected void makeLayerInfosPanel(Collection<LayerInfo> layerInfos) { // JPanel layersPanel = new JPanel(new GridLayout(0, 1, 0, 4)); // layersPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); JPanel layersPanel = new JPanel(); BoxLayout layout = new BoxLayout(layersPanel, BoxLayout.Y_AXIS); layersPanel.setLayout(layout); layersPanel.setFont(new Font("宋体", Font.PLAIN, 10)); // Add the server's layers to the panel. for (LayerInfo layerInfo : layerInfos) { addLayerInfoPanel(layersPanel, SmartScopeWMSLayersPanel.this.wwd, layerInfo); } // Put the name panel in a scroll bar. JScrollPane scrollPane = new JScrollPane(layersPanel); scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); scrollPane.setPreferredSize(size); // Add the scroll bar and name panel to a titled panel that will resize // with the main window. // JPanel westPanel = new JPanel(new GridLayout(0, 1, 0, 10)); // westPanel.setBorder(new // CompoundBorder(BorderFactory.createEmptyBorder( // 9, 9, 9, 9), new TitledBorder("Layers"))); // westPanel.add(scrollPane); this.add(scrollPane, BorderLayout.CENTER); this.revalidate(); } protected void addLayerInfoPanel(JPanel layersPanel, WorldWindow wwd, LayerInfo linfo) { // Give a layer a button and label and add it to the layer names panel. LayerInfoAction action = new LayerInfoAction(linfo, wwd); if (linfo.getAbstract() != null) { action.putValue(Action.SHORT_DESCRIPTION, linfo.getAbstract()); JCheckBox jcb = new JCheckBox(action); jcb.setFont(new Font("宋体", Font.PLAIN, 14)); jcb.setSelected(false); layersPanel.add(jcb); } } protected class LayerInfoAction extends AbstractAction { protected WorldWindow wwd; protected LayerInfo layerInfo; protected Object component; public LayerInfoAction(LayerInfo linfo, WorldWindow wwd) { super(linfo.getTitle()); // Capture info we'll need later to control the layer. this.wwd = wwd; this.layerInfo = linfo; } public void actionPerformed(ActionEvent actionEvent) { // If the layer is selected, add it to the world window's current // model, else remove it from the model. if (((JCheckBox) actionEvent.getSource()).isSelected()) { if (this.component == null) this.component = createComponent( layerInfo.caps, layerInfo.params); updateComponent(this.component, true); } else { if (this.component != null) updateComponent(this.component, false); } // Tell the world window to update. wwd.redraw(); } } protected void updateComponent(Object component, boolean enable) { if (component instanceof Layer) { Layer layer = (Layer) component; LayerList layers = this.wwd.getModel().getLayers(); layer.setEnabled(enable); if (enable) { if (!layers.contains(layer)) { ApplicationTemplate.insertBeforePlacenames(this.wwd, layer); ApplicationTemplate.insertBeforeLayerName(wwd, layer, "Landsat"); this.firePropertyChange("LayersPanelUpdated", null, layer); } } else { layers.remove(layer); this.firePropertyChange("LayersPanelUpdated", layer, null); } } else if (component instanceof ElevationModel) { ElevationModel model = (ElevationModel) component; CompoundElevationModel compoundModel = (CompoundElevationModel) this.wwd .getModel().getGlobe().getElevationModel(); if (enable) { if (!compoundModel.getElevationModels().contains(model)) compoundModel .addElevationModel(model); } } } protected static Object createComponent(WMSCapabilities caps, AVList params) { AVList configParams = params.copy(); // Copy to insulate changes from // the caller. // Some wms servers are slow, so increase the timeouts and limits used // by world wind's retrievers. configParams.setValue(AVKey.URL_CONNECT_TIMEOUT, 30000); configParams.setValue(AVKey.URL_READ_TIMEOUT, 30000); configParams.setValue(AVKey.RETRIEVAL_QUEUE_STALE_REQUEST_LIMIT, 60000); try { String factoryKey = getFactoryKeyForCapabilities(caps); Factory factory = (Factory) WorldWind .createConfigurationComponent(factoryKey); return factory.createFromConfigSource(caps, configParams); } catch (Exception e) { // Ignore the exception, and just return null. } return null; } protected static String getFactoryKeyForCapabilities(WMSCapabilities caps) { boolean hasApplicationBilFormat = false; Set<String> formats = caps.getImageFormats(); for (String s : formats) { if (s.contains("application/bil")) { hasApplicationBilFormat = true; break; } } return hasApplicationBilFormat ? AVKey.ELEVATION_MODEL_FACTORY : AVKey.LAYER_FACTORY; } protected static String makeTitle(WMSCapabilities caps, LayerInfo layerInfo) { String layerNames = layerInfo.params.getStringValue(AVKey.LAYER_NAMES); String styleNames = layerInfo.params.getStringValue(AVKey.STYLE_NAMES); String[] lNames = layerNames.split(","); String[] sNames = styleNames != null ? styleNames.split(",") : null; StringBuilder sb = new StringBuilder(); for (int i = 0; i < lNames.length; i++) { if (sb.length() > 0) sb.append(", "); String layerName = lNames[i]; WMSLayerCapabilities lc = caps.getLayerByName(layerName); String layerTitle = lc.getTitle(); sb.append(layerTitle != null ? layerTitle : layerName); if (sNames == null || sNames.length <= i) continue; String styleName = sNames[i]; WMSLayerStyle style = lc.getStyleByName(styleName); if (style == null) continue; sb.append(" : "); String styleTitle = style.getTitle(); sb.append(styleTitle != null ? styleTitle : styleName); } return sb.toString(); } protected void makeProgressPanel() { // Create the panel holding the progress bar during loading. JPanel outerPanel = new JPanel(new BorderLayout()); outerPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); outerPanel.setPreferredSize(this.size); JPanel innerPanel = new JPanel(new BorderLayout()); innerPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); JProgressBar progressBar = new JProgressBar(); progressBar.setIndeterminate(true); innerPanel.add(progressBar, BorderLayout.CENTER); JButton cancelButton = new JButton("Cancel"); innerPanel.add(cancelButton, BorderLayout.EAST); cancelButton.addActionListener(new AbstractAction() { public void actionPerformed(ActionEvent actionEvent) { if (loadingThread.isAlive()) loadingThread.interrupt(); Container c = SmartScopeWMSLayersPanel.this.getParent(); c.remove(SmartScopeWMSLayersPanel.this); } }); outerPanel.add(innerPanel, BorderLayout.NORTH); this.add(outerPanel, BorderLayout.CENTER); this.revalidate(); } }