1. 程式人生 > >帶你剖析WebGis的世界奧祕----點和線的世界

帶你剖析WebGis的世界奧祕----點和線的世界

前言

昨天寫了好久的博文我沒儲存,今天在來想繼續寫居然沒了,氣死人啊這種情況你們見到過沒,所以今天重新寫,我還是切換到了HTML格式的書寫上大笑。廢話不多說了,我們現在就進入主題,上週我仔細研究了WebGis基於openlayers的顯示問題,同事也略微的實現了地圖上的點選事件當然啦,這周我們將細分為點和線的點選事件,如果讀者有興趣也可以自己研究區域的點選事件,說白了就是我們初中時候學的那個三維立體的思想,或者讀者也可以私下找我交流。
邏輯思想

(點)在處理這些點選事件中我突然想到一個很常見的思路,就是我通過點選來獲取螢幕的座標,然後在將螢幕的座標轉換成對應地圖上的經緯度,拿到經緯度之後去和資料庫裡面一條一條匹配,最後從資料查到這個點了我就將這個點的資訊給輸出,如果我沒有查到這個點就說明我沒有點選到這個點上。但是值得注意的一點就是我們地圖在螢幕上是很小的,是存在點選誤差的,什麼叫點選誤差就是我們點選地圖上的點我們肉眼上覺得是點選了點,但是實際上我們點選的那個點的座標和地圖上的那個點的座標是存在一定的誤差的,所以我為了實現肉眼上的點選點的事件,我在查詢資料庫的時候給定了一個我們肉眼預設的誤差範圍的,也就是說我在資料中並不是真正去查這個點,而是查詢資料庫中的點到我這個店的距離的,只要這個距離小於我指定的誤差值,我就預設為這兩個點是同一個點,這也就是我實現點的思路;那麼問題來了如果我在地圖上點選的時候出現了兩個點都和我這個點的距離在我的誤差範圍之內呢,答案是肯定會的,那麼有的讀者會問,這個該怎麼辦呢。不用擔心我有解決方法。在取到不止兩個的情況下我們去距離最小的那一個,離的最近的我們預設選擇這個點的

(線)處理完點自然就是線了,在處理線的時候我一開始的思路就是拿角度去比對,如果是統一角度的就說明我們點選的點在這條線上面,如果不是統一角度就不在這條線上,這個方法後來我實行了,當然這個也是存在誤差的,我指定好了誤差之後就可行的,但是在效能上不好,沒有在距離的方法有效,因為如果我們用角度的話,角度會根據大小的不同誤差級別,也就是說角度本身就有誤差,在加上我們的肉眼的誤差這樣就相當於放大了誤差,所以最後在專案裡我放棄了角度的方法,後來我換了一種思路,我可以求點到直線的距離啊,這樣我就成功的將角度的問題轉換成了上面的距離的問題了,而且高中我們都會用點到直線的距離公式,而且倆個點確定直線的方程我們也都會使用最後整理了一下就是下面的程式碼來實現球點到直線的距離,拿到了距離了就回歸了我們上面的點的處理中了,我們只要指定誤差範圍就可以了。
(總結)理論呢就是這麼多,估計有的同學已經開始著急了,怎麼還沒有程式碼啊,個人覺得!授人以漁不如授人以漁。所以我在上面才嘮叨半天,只要你們理解我的思路,下面的程式碼只需要有初中的水平就可以看得懂了。
/續上/
程式碼實現

1、在map地圖上我們註冊點選事件,唯一不同的是我們在這裡自行區分點線

}

map.events.register("click", map, function(e) {
//編寫點選事件

2、獲取螢幕座標從而轉向地理座標
var lonlat = map.getLonLatFromPixel(e.xy);
3、通過地理座標去和已有的資料進行匹配查詢,這裡說的已有資料是說資料庫中已經存在的點和線的資料,我現在js模擬一下資料庫的寫法(data就是資料裡的資料)


if(data.length>1){
    var min=getDis(data[0].x, data[0].y, data[1].x, data[1].y, lonlat.lon, lonlat.lat);
    var index=0;
    for(var i=0;i<data.length;i++){
    <span style="font-family:SimHei;">    </span>if(i<data.length-1){
        var fx = data[i].x;
        var fy = data[i].y;
        var tx = data[i+1].x;
        var ty = data[i+1].y;
    <span style="font-family:SimHei;">    </span>}
    var dis = getDis(fx, fy, tx, ty, lonlat.lon, lonlat.lat);
        if(dis<min){
                min=dis;
                index=i;
          }
    }
    var wc = judgediswc(map.getZoom());
        if(wc>=min){
                
        //在誤差範圍內,可以認為是同一線
        var lineName = data[index].site_name+">"+data[index+1].site_name;
        //alert(lineName);
        $("#qds").val(lineName);
        }else{
          searRailLine(lonlat.lon, lonlat.lat);
        }
    }else{
        searRailLine(lonlat.lon, lonlat.lat);
    }

4、這裡提到的getDis 和 judgediswc 還有searRailLine這幾個方法都是自己去寫的,他們的作用是分別計算點到直線的距離、點到直線距離和誤差的比較、去真實資料庫查詢線的方法。下面進行這三個方法的講解
4-1、獲得點到直線的距離,這個就是我們初高中經常用到的點到直線距離公式,自己稍加推到就可以看得懂了,這個沒有什麼技術含量,只有別把座標位置放錯了,就可以了,這個返回的dis就是我們需要的距離。這個演算法我在資料裡也封裝好了,待會最後我會帖進來的。

function getDis(fx,fy,tx,ty,lon,lat){
    var dis = 637800.138 * (
            Math.abs(
            (
            (ty - fy) * lon
            ) + (
            (fx -
                    tx) *
            lat
            ) + tx * fy - ty * fx
            )
            ) / (
                    Math.pow(
            (ty
            - fy) * (ty - fy) + (fx -
                    tx) * (fx -
                            tx),
            0.5
            )
            );
    return dis;
}

4-2、誤差的大小(由於地圖的縮放級別不同,我們的誤差也不同,這個在前面的理論中我已經解釋過了,這個就不多說了)這個就返回我們肉眼能夠接受的誤差最大值

function judgediswc(level){
    var dis=0;
    var angle=0;
    if(level==15){
        dis=12;
        angle=42;
    }else if(level==14){
        dis=16;
        angle=94;
    }else if(level==13){
        dis=52;
        angle=149;
    }else if(level==12){
        dis=69;
        angle=403;
    }else if(level==11){
        dis=178;
        angle=479;
    }else if(level==10){
        dis=294;
        angle=1093;
    }else if(level==9){
        dis=682;
        angle=1449;
    }else if(level==8){
        dis=1488;
        angle=4143;
    }else if(level==7){
        dis=3521;
        angle=4617;
    }else if(level==6){
        dis=6636;
        angle=5265;
    }else if(level==5){
        dis=7051;
        angle=30217;
    }else if(level==4){
        dis=25763;
        angle=68126;
    }else if(level==3){
        dis=60342;
        angle=120000;
    }else if(level==2){
        dis=88189;
        angle=220000;
    }else if(level==1){
        dis=110189;
        angle=1000000;
    }
    return angle;
}

4-3、下面的方法我們就是真正去資料庫查詢的,上面的方法沒有實際用處,只是為了掩飾給讀者的快捷方法(注意我ajax的傳參 很重要)


function searRailLine(lon,lat){
    var sus = function(json, status) {
        //alert();
        var lineName = json.map[0].station_name+"站"+">"+json.map[0].next_name+"站";
        $("#qds").val(lineName);
        siteJson = json.map;
    };
    AjaxUtil.ajaxCon(path + "/echartMap/selectLineNameStr.json", {lon:lon,lat:lat,angle:judgediswc(map.getZoom())},
            sus);
}

5、具體的三層架構實現我就不寫了,相信能看到這裡的孩子們三層架構一定都瞭解。我直接貼sql語句(x,y,z分別是經度、緯度、地圖的顯示級別)方法中返回的是點到直線的最小距離(有可能有很多個直線都是符合的我們取最小的那個)

 <select id="selectLine" resultType="java.util.HashMap">
        SELECT
        line_name,
        station_name,
        station_x,
        station_y,
        next_name,
        next_x,
        next_y,
        t
        FROM
        (
        SELECT
        line_name,
        station_name,
        station_x,
        station_y,
        next_name,
        next_x,
        next_y,
        637800.138 * (
        ABS(
        (
        (station_y - next_y) * #{x}
        ) + (
        (next_x -
        station_x) *
        #{y}
        ) + station_x * next_y - station_y * next_x
        )
        ) / (
        POWER(
        (station_y
        - next_y) * (station_y - next_y) + (next_x -
        station_x) * (next_x -
        station_x),
        0.5
        )
        ) AS t
        FROM
        rail_line
        ) AS t
        WHERE
        t
        < #{angle}
        AND station_y >= #{y}
        AND next_y <= #{y}
        order by t asc limit 0,1
    </select>

6、有的讀者會問了,說好的點和線呢,你的點呢,彆著急啊,我先把難的講完簡單,點呢我就把sql語句貼出來,返回了距離的值,在後臺你想怎麼辦就怎麼辦,那就看你自己了。對吧


 <select id="selectPosition" resultType="String" parameterType="HashMap">
    <![CDATA[   
        SELECT
            t.name
        FROM
            (
                SELECT
                    x.name,
                    ROUND(
                        6378.138 * 2 * ASIN(
                            SQRT(
                                POW(
                                    SIN(
                                        (#{y} * PI()
        / 180 - y * PI() / 180) / 2
                                    ),
                                    2
                                ) + COS(
                                    #{y} * PI() / 180) * COS(y *
                                    PI() / 180
                                ) * POW(
                                    SIN(
                                        (
                                            #{x} * PI() / 180 - x * PI() / 180
                                        ) / 2
                                    ),
                                    2
                                )
                            )
                        ) * 1000
                    ) AS dis
                FROM
                    (select  SITE_NAME name ,fx x,fy y from TKYZ_BASE
                                UNION 
                                select SITE_NAME name,fx x,fy y from THYZ_BASE
                                UNION
                                select SITE_NAME name,fx x,fy y from TKHYZ_BASE
                                UNION
                                select SITE_NAME name,fx x,fy  y from TYXHR_BASE
                                UNION
                                select SITE_NAME name,fx x,fy y from TBZQD_BASE)x
            ) AS t
        WHERE
            t.dis BETWEEN 0
        AND #{z}
        order by t.dis asc limit 1
        ]]>
    </select>

效果貼圖

1、途中高亮顯示的是模擬的js中的資料,點選高亮部分就會在指令碼中查詢我模擬的資料

2、點選高亮線路(北京--》北京南--》豐臺)跳出相應的線路資訊

3、點選非高亮地區會去查詢資料庫中的資料,就是真正的資料,如果點選在線上了也會彈出線的詳細資訊

4、如果我們點選在點上面就會 是點的相應資訊,就不貼出來了,在高亮的線上我留個bug就是線上的延長線上也是認為在線上的,實際上不應該認為是線上,這點由於時間緊急我當時就沒有寫,讀者有興趣自己補充,提示一下判斷點的位置是否線上的兩端的中間就可以了,這個功能在點選非高亮線的sql語句我實現了的,可以參考一下。
總結

研究了openlayers也有兩週了,這兩週學到的東西很多。學東西一定要從底層學,才可以學到真正的東西。openlayers的幾個基本功能我都已經實現了(地圖顯示+點線點選事件+定位點+定位線+居中顯示墨點+手型顯示),其他一些實時定位並隨地理位置變動而變動的這個功能沒有去實現,因為就兩週時間,也就這樣了,之間走了很多的彎路,所以發出博文希望對學習WebGis的新同學有點幫助

加入戰隊

# 加入戰隊

微信公眾號

相關推薦

剖析WebGis世界奧祕----世界

前言 昨天寫了好久的博文我沒儲存,今天在來想繼續寫居然沒了,氣死人啊這種情況你們見到過沒,所以今天重新寫,我還是切換到了HTML格式的書寫上大笑。廢話不多說了,我們現在就進入主題,上週我仔細研究了WebGis基於openlayers的顯示問題,同事也略微的實現了地圖上的點選事件當然啦,這周我們將細分為點和線的

循序漸進學習時間復雜度空間復雜度。

準備 日常 數據結構 media 輸入數據 有時 學習過程 幫助 和平 本文字數:4894 字 閱讀本文大概需要:13 分鐘 寫在之前 我們都知道,對於同一個問題來說,可以有多種解決問題的算法。盡管算法不是唯一的,但是對於問題本身來說相對好的算法還是存在的,這

徹底看懂React NativeAndroid原生控制元件之間的對映關係

此文基於react natve的 September 2018 - revision 5 版本 本人學校畢業後就當了安卓爬坑專業戶,3年來總算爬習慣了,不料今年掉進了RN這個天坑,從此開始了我的悲慘人生。。。Anyway,RN的思想還是值得學習的,今天就從Android的角度開始分析一下react nati

Python:走進哈利波特的魔法世界

最近有將近一個月的時間沒更新了,並不是小文有心地偷懶,而是實在是有太多的磚要搬了(不多說了,等會還要繼續搬-_-!!)......因為最近的專案涉及到文字分析(jieba包)以及人物關係分析(gephi),因此今天就整理總結一下,以哈利波特七部曲(國慶假期看的)為例,分享一下

架構師實踐日 11.9 南京站報名 | 技術大牛剖析大資料平臺內部演進中的挑戰與實踐

從網際網路時代到物聯網時代,資料成為了企業的核心資產,挖掘資料價值成為了企業資料探索、技術應用的重中之重,甚至將影響到企業未來的發展和商業模式。但大資料體量大、多樣性、價值密度低、速度快等特徵,也給大資料的應用研發工作帶來了不少挑戰。  如何應對大資料

C#--三行程式碼理解神祕的拆箱裝箱

一、在說拆箱和裝箱之前的準備知識 首先,我們需要知道C#中有兩種型別:值型別和引用型別 名稱 值型別 引用型別 表示型別 基本型別 類,陣列,介面 ,C#特有的委託. 儲存內容 值 值的引用 儲存位

深入理解STL之StackQueue

上一篇部落格,帶你深入理解STL之Deque容器中詳細介紹了deque容器的原始碼實現方式。結合前面介紹的兩個容器vector和list,在使用的過程中,我們確實要知道在什麼情況下需要選擇恰當的容器來滿足需求和提升效率。一般選擇的準則有如下幾條: 如果需要隨

Android一步一步實現RecyclerView的拖拽側滑刪除功能

先上效果圖: 本篇文章我們來學習一個開源專案Android-ItemTouchHelper-Demo 這個專案使用了RecyclerView的ItemTouchHelper類實現了Item的拖動和刪除功能,ItemTouchHelper是v7包下的一個類

在ArcEngine中建立高程Z值的圖層

管線和其附屬物的座標資料都是帶有Z值的 而且有些情況下,一個管段的兩個端點的x,y值一模一樣(垂直的管段) 這樣的線,在直接生成shape圖層的時候,就會產生問題,特別是 使用ArcSDE的C API直接建立到表中的時候你會發現,這樣的資料是生成不了的 解決的方法就是為圖層

最小生成樹演算法【圖解】--一文理解什麼是Prim演算法Kruskal演算法

假設以下情景,有一塊木板,板上釘上了一些釘子,這些釘子可以由一些細繩連線起來。假設每個釘子可以通過一根或者多根細繩連線起來,那麼一定存在這樣的情況,即用最少的細繩把所有釘子連線起來。 更為實際的情景是這樣的情況,在某地分佈著N個村莊,現在需要在N個村莊之間修路,每個村莊之前的距離不同,問怎麼修最短的路,將各個

OpenCV開發筆記(六十五):紅胖子8分鐘深入瞭解ORB特徵(圖文並茂+淺顯易懂+程式原始碼)

若該文為原創文章,未經允許不得轉載原博主部落格地址:https://blog.csdn.net/qq21497936原博主部落格導航:https://blog.csdn.net/qq21497936/article/details/102478062本文章部落格地址:https://blog.csdn.ne

2w+長文剖析ConcurrentHashMap~!

併發程式設計實踐中,ConcurrentHashMap是一個經常被使用的資料結構,相比於Hashtable以及Collections.synchronizedMap(),ConcurrentHashMap線上程安全的基礎上提供了更好的寫併發能力,但同時降低了對讀一致性的要求(這點好像CAP理論啊 O(∩_∩)

Python視覺化中Matplotlib(3.線條的詳細樣式及線性、儲存圖片、plot的詳細風格樣式)、背景色、的詳細設定

1.修改線條的樣式: linestyle、color、marker(標記) ''' 顏色 color:修改顏色,可以簡寫成c 樣式 linestyle='--' 修改線條的樣式 可以簡寫成 ls 標註 marker : 標註 線寬 linewidth: 設

ArcGIS for Android 100.3的學習與應用(二) 如何移除指定的

在地圖上新增點和線的時候,我們有時候會遇到要移除或者切換指定的點和線的操作。那麼如何移除指定的點和線呢? ArcGIS的api裡點和線都是由GraphicsOverlay類來進行建立新增的。通過Graphic物件將點或者線的圖形物件(SimpleMarkerSymbol,SimpleLine

ArcGIS for Android 100.3的學習與應用(一) 如何繪製

平時工作中,我們接觸到的地圖類開發平臺有很多,最常用的有高德,百度,騰訊地圖。而且它們都有自己的開發者平臺和文件供我們使用。基本能滿足我們的業務需求。 由於公司裡的專案會涉及一些地圖資料統計和展示方面的需求,同時也會發布一些地圖服務,所以選擇了使用在地圖方面比較牛逼的ArcGIS。 把平時遇

OSG拾取當然面也能拾取

if (ea.getButton() == osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON) { osg::ref_ptr<osgUtil::Polytop

Android ArcGIS的學習與應用(一) 如何繪製

平時工作中,我們接觸到的地圖類開發平臺有很多,最常用的有高德,百度,騰訊地圖。而且它們都有自己的開發者平臺和文件供我們使用。基本能滿足我們的業務需求。 由於公司裡的專案會涉及一些地圖資料統計和展示方面的需求,同時也會發布一些地圖服務,所以選擇了使用在地圖方面比較

ArcGIS for Android 的學習與應用(二) 如何移除指定的

在地圖上新增點和線的時候,我們有時候會遇到要移除或者切換指定的點和線的操作。那麼如何移除指定的點和線呢? ArcGIS的api裡點和線都是由GraphicsOverlay類來進行建立新增的。通過Graphic物件將點或者線的圖形物件(SimpleMarkerSy

Problem : 平面上的——Point類、Line類 (III)

Point : (1, -2) is created.Point : (2, -1) is created.Point : (0, 0) is created.Point : (0, 0)=========================Point : (0, 0) is created.Point :

OpenLayers3入門篇-建立

地圖是由圖層組成的,而圖層又分為ol.layer.Tile和ol.layer.Vector,ol.layer.Tile可以構建底圖,ol.layer.Vector指定向量圖層,畫點畫線或者建立其他基本就是建立ol.layer.Vector,ol.layer.Vecto