去年底第一次參加 g0v 黑客松我的作品就是選舉地圖 (2012/12/2 ,今天正好一年 XD)
把各村里投開票所藍綠的得票數配上村里界圖作出的地圖呈現
不過當初做這個有個很大的缺點,就是村里界圖是一個接近 3MB 的 GeoJSON,要 load 進瀏覽器來處理真的會耗掉很多資源
而且 3MB 已經是把精細度壓到很低了,應該是到十公尺以上的精細度了吧,原始資料是 60MB
像是 GitHub 最近有支援預覽 GeoJSON 的功能,也是在瀏覽器上直接計算,但是有 1MB 的限制,要不然真的會很慢又吃掉不少頻寬
所以最近幾個月我開始想要轉換作法,改成在 server 端作出圖片,然後疊在現有地圖的作法

新的成品就在 http://github.ronny.tw/ronnywang/sandbox/blob/master/20131126/2012-president-color.json
這邊是利用 Google Map Javascript API 的 ImageMapType ,他會把顯示出來的地圖上面,切成 256px * 256px 的 tile(大小可自行設定) ,使用者捲到哪裡,API 就自動去呼叫 getTileURL 要去畫出哪些 tile
而 getTileURL 裡面的參數 coord 就可以取出這個 tile 的左上和右下的經緯度分別是多少,所以後端伺服器只要畫出這一塊區域的圖型就行了

我目前寫出來的一個 Tile 的 PNG 依複雜度會是 1kb ~ 50kb 不等(看上面的資訊量)
圖片長的會像下面一樣


網址:d4ru4jg0u8swg.cloudfront.net/wms?Request=GetMap&Layers={"type"%3A"colormap"%2C"set_id"%3A27%2C"tab"%3A"\u85cd\u7da0\u5730\u5716"}&BBox=119.9267578125,23.32208001137843,121.025390625,24.32707654001865&Width=400&height=400

一個 Tile 就會如上,從網址看的出來是要畫出經緯度界於 (119.926E, 23.322N) 到 (121.025E, 24.327N) 之間的資訊,並輸出成 400x400 的圖檔
改用 Tile 真的也能省下不少頻寬,想想看原先 60MB 的 GeoJSON 省下來,就足以傳超過一萬張 tile 的圖片了
再加上 Tile 圖片可以過 CDN cache 起來,這樣子後端 server 就可以很閒只要畫一次就好了

而完成這功能的最主要核心有兩個,一個是透過 PostgreSQL 的 PostGIS 如何快速取得需要的經緯度之間的資訊,以及如何把這些資訊畫成 PNG 傳回來
前者我之後會再找時間寫成文章
後者的話我已經把程式 open source 在 GitHub: https://github.com/ronnywang/geojson2image
只要丟一個 GeoJSON 進去就可以輸入 PNG
唯一用到的函式庫是 PHP-GD
詳細用法我就不在這邊說明了,有興趣的人就翻 GitHub repository 內的說明吧

為了做到這件事我遇到一些問題並且解決掉
遇到的問題包括
1. 地球是圓的
我最早的作法是假如要把 (119.926E, 23.322N) 到 (121.025E, 24.327N) 之間的資訊畫到 400x400 的圖檔上去
就表示目標圖檔的 1 個 pixel 會是 (121.025-119.926)/400 和 (24.327-23.322)/400 的資訊
這作法如果只處理台灣的話沒什麼問題
但是當一處理到世界地圖時就爛掉了
(為什麼地球要是圓的啦~是平的有多好!)

原因是在不同緯度,每1經度的寬度都是不一樣的,像是赤道上的1經度是最大的,而極點上的1經度就是接近0
解決辦法就是要加上 sin, cos 來計算了
這應該是高中三角函數的程度,但是脫離學校太久我已經懶得計算這些 XD
所以直接找有沒有 code sample ,找到了一篇Bing Maps Tile System 裡面有(謝謝微軟 Q_Q)
就解決這個問題了

2. 經度是有可能 +180 跨到 -180 的
如果沒處理到這個問題的話,就會發生明明座標是 +179E => -179W 只走 2 個經度,圖卻畫成走了 358 個經度
這邊我的解決方式是先把經度都加上 180 ,讓他座標系從 -180 => 180 變成 0 => 360 比較好計算
然後如果遇到需要畫的區間經度是在 +XXX 到 -YYY 的話
我就把他分成 +XXX ~ +180 和 -180 ~ -YYY 兩張圖再組合在一起
就解決跨經度的問題了

GeoJSON2Image 這個 PHP class 寫法滿單純的,沒用到太多 PHP 的奇技淫巧,大部份都是 foreach 加上四則運算
所以也歡迎轉換成其他語言喔!
arrow
arrow
    全站熱搜

    榮尼王 發表在 痞客邦 留言(0) 人氣()