大量POI點展示的一種解決方案

概述:

不論是在Arcgis for js還是Openlayers中,當POI點比較多的時候,在前臺頁面的展示在效率上是一大問題。經過一段時間的研究,發現百度地圖在這一問題上的處理思路比較好:將要展示的POI點在服務器端生成圖片,頁面只調用圖片的話效率會比較高。本文講述如何在java後臺實現POI點在服務器端的實時生成以及在Openlayers2的展示。

實現後效果:

技術難點:

要實現POI點在服務器端的生成,難點在與如何通過前臺請求的參數計算出座標點的屏幕座標。爲此參了網上的一篇文章解決了此問題,文章內容如下:

地理座標定義規則:X軸(代表經度)向右遞增,Y軸(緯度)向上遞增,就好比小學學過的平面座標。向左、向下的規則。屏幕座標定義規則:X軸向右遞增,Y軸向下遞增。
可以看出,地理座標和屏幕座標的區別僅僅只是在於Y軸遞增方向是相反的(這就是不同)。
這裏強調一點的就是爲了保證精度,地理座標的度*3600換算成秒,所有的取值用double來計算,最後的結果再轉換成int。
1 已知道屏幕的高(y)和寬(h),地理座標區域的範圍(maxLon,minLon,maxLat,minLat),這裏我們知道了這些已知的參數。
2 我們可以算出每像素所代表的經度和緯度(有人稱這個爲比例因子)。
公式:scaleX = ((maxLon-minLon)*3600)/h ----------X軸上每像素代表的經度秒數;
公式:scaleY = ((maxLat-minLat)*3600)/y ----------Y軸上每像素代表的緯度秒數;
這兩個比例因子就是兩個座標系之間的關係。
3 很簡單的一步了,那就是算出該地理座標區域中的任何一點(lon,lat)在屏幕上的座標了。
公式:screenX = lon*3600/scaleX;---------屏幕座標X軸座標
公式:screenY = lat*3600/scaleY; ---------屏幕座標Y軸座標
還有最後一步,那就是我們要把該地理區域佔滿佔個屏幕該怎麼辦呢?
4 接着我們需要該地理區域佔滿佔個屏幕該怎麼辦呢
公式:minX = minLon*3600/scaleX;區域左邊置最左端
公式:minY = minLat*3600/scaleY; 區域上面置最上端
5 當地地理範圍區域佔滿整個屏幕時,我們需要用到第三步計算出來的 screenX和screenY兩個參數,該區域中的任何一點的公式如下: 
公式:X = screenX - minX = (lon - minLon)*3600/scaleX; 
公式:Y = screenMaxLat - screenLat = (maxLat - lat)*3600/scaleY;
6 總結:
經緯度轉屏幕座標的最終公式如下:
公式:X = (lon - minLon)*3600/scaleX; 
公式:Y = (maxLat - lat)*3600/scaleY;
接着我們由上面的公式可以推出屏幕座標轉經緯度座標公式如下:
公式:lon = X * scaleX/3600 + minLon;
公式:lat = maxLat - y* scaleY/3600;

編碼實現:

後臺POI圖片的實時生成用了一個servlet實現,前臺調用用WMS來調用,具體代碼如下:

package com.lzugis.web;


import javax.imageio.ImageIO;

import java.awt.Color;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class PoiServices
 */
@WebServlet(description = "poi to wms", urlPatterns =  {"/map/poi"})
public class PoiServices extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see javax.servlet.http.HttpServlet#HttpServlet()
     */
    public PoiServices() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		this.doPost(request,response);
	}

	/**
	 * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub		
		 String bbox= request.getParameter("BBOX");
		 String width= request.getParameter("WIDTH");
	     String height= request.getParameter("HEIGHT");
	     int w = Integer.parseInt(width),
	    	  h = Integer.parseInt(height);
	     String[] extent = bbox.split(",");
	     double xmin = Double.parseDouble(extent[0]),
	    		 ymin = Double.parseDouble(extent[1]),
	    		 xmax = Double.parseDouble(extent[2]),
	    		 ymax = Double.parseDouble(extent[3]);
	     double scalex = ((xmax-xmin)*3600)/w,
	    		 scaley = ((ymax-ymin)*3600)/h;
	     List<String> geoData = new ArrayList<String>();
	     /*geoData.add("87.5758285931,43.7822116460");
	     geoData.add("91.1629975040,29.7104204643");
	     geoData.add("116.4575803581078,40.04054437977018");
	     geoData.add("103.584297498,36.1190864503");*/ 
	     geoData.add("116.294,39.9742"); 
	     geoData.add("116.306,39.9754"); 
	     ……
	     
	     BufferedImage image = new BufferedImage(w, h,BufferedImage.TYPE_INT_RGB);
	     java.awt.Graphics2D g2d = image.createGraphics();
	     image = g2d.getDeviceConfiguration().createCompatibleImage(w,h,
	    	java.awt.Transparency.TRANSLUCENT);
	     g2d.dispose();
	     g2d = image.createGraphics();
	     for(int i=0;i<geoData.size();i++){
	    	 String lonLat = geoData.get(i).toString();
	    	 String lon = lonLat.split(",")[0],
	    			 lat = lonLat.split(",")[1];
	    	 double x = Double.parseDouble(lon),
	    			 y = Double.parseDouble(lat);
	    	 double scrx = (x-xmin)*3600/scalex,
	    			 scry = (ymax-y)*3600/scaley;
	    	 Image img = ImageIO.read(new File("c:/icon.png"));
	    	 g2d.drawImage(img, (int)scrx, (int)scry, null, null);
	     }
	     g2d.setStroke(new java.awt.BasicStroke(10));
	     // 釋放對象
	     g2d.dispose();
	     // 保存文件
	     OutputStream os = response.getOutputStream();
	     try {
	    	 String poiimg = "c:/wms.png";
	    	 ImageIO.write(image, "png", new File(poiimg));
	         int count = 0;
	         byte[] buffer = new byte[1024 * 1024];
             InputStream inStream = new BufferedInputStream(new FileInputStream(poiimg));
	         while ((count = inStream.read(buffer)) != -1){
	             os.write(buffer, 0, count);
	         }
	         os.flush();	        
	         inStream.close();
	         os.close();
	     }
	     catch (IOException e) {
	          e.printStackTrace();
	     }
	}
}

說明:

此處用了北京市的地鐵站點的數據作爲測試數據。

前臺調用:

                var poiurl = "http://localhost:8081/lzugis/map/poi";
                var poilayer = new OpenLayers.Layer.WMS("poilayer",
                        poiurl,
                        {
                            layers: "",
                            transparent: true
                        }, {
                            opacity: 1,
                            singleTile: true
                        });
                map.addLayer(poilayer);

後續:

作爲POI,是需要有鼠標的事件的,這部分的內容正在研究中,後期會陸續更新。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章