羅孚想要制作一份疫情場所分布地圖,最初是因為看到我所在的城市(上海)疫情場所位置不准,想自己做一份數據並顯示在地圖上,結果一拖再拖然后疫情都快結束了[尷尬],不過我還是花了一天時間研究了一下,於是有了這篇《如何制作一份疫情場所分布地圖?》,要不就當羅孚手把手教你制作一份疫情場所分布地圖吧[捂臉]。
本文是以上海地圖數據為基礎、基於高德地圖API設計制作的一份城市地圖,如果你收集了你所在城市的地圖數據,那么按照本文文末提供的源碼和數據結構,你也可以直接復制出一份城市疫情場所地圖。要是你真實現了,記得要回來show給我看一下哦。
功能和效果預覽

上圖是幾個主要功能的預覽
不過只給個圖不給體驗,這不是羅孚的風格,直接上體驗網址:https://rovertang.com/map/shncov/
疫情場所分布地圖現有的功能:
- 地圖顯示
- 疫情場所顯示
- 個人位置顯示
- 地圖搜索
- 其他:縮放控件顯示、地圖范圍限定等
由於是初版,僅僅研究探討,所以更強大的功能還無法實現,不過我會在下文中展望一下更強大的功能。
概要設計
兩個文件就實現了全部功能,還要做個概要設計?不好意思,請讓羅孚裝一下[捂臉]。實際上,羅孚在這事情上確實走了彎路。
實現框架
羅孚最開始考慮的實現方案,超級簡單,簡單到甚至不需要寫代碼和制作數據文件。
不管是高德、百度還是騰訊,都提供了地圖數據管理方案,高德的雲圖、百度地圖的LBS雲、騰訊地圖的地點雲,直接使用他們的雲存儲,然后在地圖上添加一個自定義雲圖層或者通過數據檢索API取出數據后marker到地圖就行了。
雲數據方案是好的,但羅孚使用下來,竟然都不好用,要么無法在地圖上顯示,要么API接口不返回數據,只能說羅孚作為一個非專業開發人員,確實太菜了一些。當然,也要批評一下高德,地圖如果是3D模式時,無法使用雲地圖圖層,這一信息是在某一文檔的一行小字中找到的,擦,這坑挖的夠深。
自建數據庫成了唯一的選擇。
不過,為了讓方案簡單易復制,使用MySQL似乎重了一點,要不使用sqlite吧。但一想到要寫數據庫的增刪改查,又增加了不少麻煩,為了快速實現,直接將最終數據做成了json文本,數據庫的操作,以后再考慮吧。
大言不慚的說,最后的實現框架是:json+js。嗯,兩個文件,一個json數據文件,一個js文件(寫在html文件里了)。[憨笑]
數據的定義和來源
對於地圖數據,必須的字段可能有:
- 名稱
- 經度
- 緯度
- 地址(可選)
對於疫情,可能的字段有: - 次數 (有些省市會提供這個地址發生的次數)
- 類型 (可以細分為小區、商場等)
- 時間 (發現時間)
- 來源 (數據源來自哪里)
呃,考慮的是不是有點復雜?羅孚實際上也沒有用這么多數據內容,數據字段越多,內容越豐富,實現難度也就越大,作為一個DEMO,就只考慮必須的名稱(實際是用地址代替的)和經緯度吧。
定義完字段,就要制作數據了,這些點位數據怎么來呢?逃不出兩種方式:
- 自己收集和標注
- 扒別人的位置數據
羅孚的初衷是想自己收集和標注,自力更生是我國人民的傳統美德,但本人懶癌嚴重,所以最后也是取了個巧,扒了份別人的數據(僅使用了上海部分啊,其他城市的沒要)。
當然,如何扒其他平台的數據,似乎又可以寫一篇文章,考慮同本教程有較大出入,加上這見不得人的伎倆,以后再說吧。
地圖平台的選擇
確定好框架,定義好數據,可以開始開發了,但由於和地圖相關,在此多討論一下地圖平台的問題。
一般來說,使用商業公司成熟的地圖API是最佳選擇:
高德和百度地圖的API:推薦。畢竟是國內不分伯仲的頂尖API。
騰訊地圖API:可用。可能可以算國內老三,但用的人真少,看在微信、小程序都是鵝廠的產品,也是值得考慮的,看了下API,也基本能用。
mapbox API:可用,有難度。mapbox是開源地圖解決方案公司,屬於國際頂尖(在地圖上甚至比Google還強大哦),現在正在大力發展國內市場,此次制作的疫情地圖也超級漂亮(欣賞一下)。
Google maps API:不能用。雖然國際頂尖(地圖API鼻祖),但不能用的道理,你懂得。(有興趣的可以了解一下我十年前拆解的Google Maps API v3離線開發包)
除了商業公司的方案,當然也可以使用開源方案,比如leaflet。我看到天地圖上海做的地方疫情地圖就是用了leaflet。開源方案主要是功能框架,但地圖底圖數據,要么用商業公司的,要么就還是自己扒了(羅孚也扒過:Google Maps瓦片(tile)地圖文件下載(1-11層級),今年過年太無聊,又扒了更多更細的數據[捂臉])。
地圖平台的探討,真可以再寫幾篇文章,對於本文的地圖平台,最終選擇了高德,主要原因是站在別人肩膀上的選擇。百度地圖API雖然我想用,但考慮到百度地圖坐標是在GCJ02坐標系下又做了一次加密,想到后續的數據坐標混亂,還是罷了罷了。
詳細設計(功能實現)
繼續大言不慚的說說功能實現的詳細設計吧,我們針對每一項功能做詳細的介紹以及實現上的注意事項,順便把我參考的地圖示例貼出來供大家參考。
地圖顯示
地圖顯示是最基本的功能,有一些地方需要注意,談談我在地圖上的一些考慮。
- 考慮到是地方地圖,那么全國地圖、世界地圖層級就不要顯示啦,所以我把地圖層級設置在了10-18之間。
- 既然是專題地圖,那么雜項就不要顯示了,比如室內地圖、地圖上的熱點等,都設置為false吧。
- 而地圖的風格,normal的色調太亮,換一個官方的遠山黛whitesmoke主題吧。
參考示例:
地圖創建:https://lbs.amap.com/api/javascript-api/example/map-lifecycle/map-show
設置地圖中心點/級別:https://lbs.amap.com/api/javascript-api/example/map/change-map-center
標准樣式主題:https://lbs.amap.com/api/javascript-api/example/personalized-map/set-theme-style
地圖類:https://lbs.amap.com/api/javascript-api/reference/map
添加疫情場所marker並彈出氣泡框

這一步算是本地圖DEMO的最核心功能了,雖說比較簡單,就是遍歷json,然后批量添加marker到地圖就可以了,但這里遇到了一個坑,在marker上綁定click事件后,竟然click任何一個marker,都是響應最后一個marker的氣泡框。
調整半天、搜索半天都沒能解決,羅孚差點想把整個DEMO都放棄,最后還是搜索到了一個小小的提示,才解決了這個問題。感慨百度搜索真心不靈。
這個問題的解決方法:使用marker的extData。
參考示例:
覆蓋物的添加與移除:https://lbs.amap.com/api/javascript-api/example/map-componets/map-overlays
點標記:https://lbs.amap.com/api/javascript-api/example/marker/marker-content
自定義圖標:https://lbs.amap.com/api/javascript-api/example/marker/custom-icon
獲取某個覆蓋物(關於extData的使用):https://lbs.amap.com/api/javascript-api/example/common/ext-data/
默認樣式信息窗體:https://lbs.amap.com/api/javascript-api/example/infowindow/default-style-infowindow
Marker類:https://lbs.amap.com/api/javascript-api/reference/overlay#marker
InfoWindow 類:https://lbs.amap.com/api/javascript-api/reference/infowindow#InfoWindow
添加自定位功能

此功能是顯示自己的位置,如果定位成功,則基於自身位置顯示半徑為1公里和3公里的圈,以了解附近范圍內大概有多少疫情場所marker。
需要注意,本處使用的是AMap.Geolocation,是高德地圖API的定位插件。
定位的一些細節邏輯:當定位成功后,原則上是需要將地圖的中心點移動到定位位置的。但如果定位信息中的citycode不是021,那么就不要移動地圖中心點。對於無法定位和定位成功返回的citycode不是021,那么仍然是以上海市人民廣場為地圖中心點。
參考示例:
瀏覽器精確定位:https://lbs.amap.com/api/javascript-api/example/location/browser-location
圓的繪制和編輯:https://lbs.amap.com/api/javascript-api/example/overlayers/circle-draw-and-edit
AMap.Geolocation插件:https://lbs.amap.com/api/javascript-api/reference/location#m_AMap.Geolocation
添加查詢功能

可以查看自身位置信息了解自身周邊的疫情情況,那么想看看某一小區或商場附近的疫情情況是否可以實現呢?這就需要用到查詢功能了。
這里我們使用高德的AMap.Autocomplete,是輸入提示插件,雖然不是地點搜索,但已經足夠用了,能出現在提示中的結果都是搜索頻率較高的結果,同時這個插件的使用也超級簡單,輸入綁定到某一輸入框,比如id為search的input,結果綁定到某一div,比如id為result的div,功能就實現了,你只要輸入內容,result中就出現結果了(簡單到讓羅孚瞠目結舌)。
完成上述操作,結果內容是無法選擇的,好在插件提供了返回結果功能,在AMap中設置一個監聽事件,選擇某一結果后就可以得到該結果的詳細信息,我主要是取經緯度信息,然后在地圖上顯示一個marker就好了。
參考示例:
輸入提示:https://lbs.amap.com/api/javascript-api/example/input/input-prompt
其他小功能
添加簡易控件,就是在地圖右下角的放大和縮小按鈕,使用AMap.ToolBar即可,是地圖操作工具條插件。
為了讓地圖范圍在某一范圍中顯示,可以給地圖設置一個bound,使用map.setLimitBounds方法即可。
參考示例:
地圖控件:https://lbs.amap.com/api/javascript-api/example/map-componets/map-with-function-control
限制地圖顯示范圍:https://lbs.amap.com/api/javascript-api/example/map/limit-map-show-range
地圖操作工具條插件:https://lbs.amap.com/api/javascript-api/reference/map-control#AMap.ToolBar
時間順序和事件的考慮
本demo中使用的數據,總量100+,不算很多,但若數據量達到1000+,那么加載的時間順序就顯得尤為重要,所以我們在地圖加載完成(使用map.on 'complete')后,再去加載json數據。
這樣做的優勢:先讓地圖顯示,然后看到地圖上出現marker,避免地圖長時間等待加載,畢竟用戶的耐心是有限的。
關於事件,由於主要是針對地圖或覆蓋物類,所以使用這些類的on成員方法實現對事件的綁定。比如上面加載json數據是綁定在map的complete事件上的,比如氣泡框的顯示是綁定在marker的click事件上的。
對於非地圖或覆蓋物類的事件,那就只能增加addListener事件了,比如對輸入提示結果的操作。
參考示例:
地圖加載完成:https://lbs.amap.com/api/javascript-api/example/map-lifecycle/map-complete
事件:https://lbs.amap.com/api/javascript-api/reference/event
輸入提示后查詢:https://lbs.amap.com/api/javascript-api/example/poi-search/search-after-enter-prompt/
延伸思考
產品思考
本文真的僅僅是一個DEMO,還有甚多的功能未能實現:
- 數據管理后台
直接使用json文本作為數據源,無法增刪改查確實是一個缺憾,需要多數據做一個管理,可以查詢地圖數據、添加數據、修改數據,這個功能下也包含了不少的小功能。 - 更多地圖顯示方式
僅僅顯示marker,顯示方法太粗糙,marker按理是在詳圖層級(比如16級及以上)才顯示的內容,可以考慮引入聚合圖和熱力圖。
比如10到12層級,可以考慮按照地區對疫情場所數據進行聚合顯示,按地區顯示每一區域總量總量的marker。
比如13到15層級,可以考慮顯示熱力圖,因為在此層級下查看疫情出現的熱度可能反而更直觀一些。 - 計算距離和提醒
在當前位置或查詢結果數據上,顯示距離最近的疫情場所和距離信息,以讓自身了解危險的程度。 - 引入其他地圖API
使用高德地圖API,雖然實現了此DEMO,但畢竟有自身的局限性,綁定在了此平台上,可以考慮使用同一份數據制作基於百度地圖API、騰訊地圖API以及leaflet庫的地圖。
還有很多很多,無法一一敘述,后續可以考慮寫一篇疫情地圖功能對比的文章,看看別人在產品制作上的優劣。
開發思考
不是開發人員,但用代碼堆起了這個DEMO,那么也說說在開發上的思考吧。
- 不要重復造輪子
站在巨人肩膀上,用成熟的API,其實挺好的。
有時候你的目標是實現功能,而不是創造一個有競爭力的平台,那么你就沒有必要思考自己開發一個平台,甚至連選擇哪個平台都不需要太多的顧慮。 - 專業的人做專業的事
羅孚本人不是程序猿,也應該有10年以上沒有碰過代碼了,這次做個DEMO都費了老半天的勁,只能感慨專業的事還是由專業的人來做吧。
不過上面產品思考的功能開發,如果在屏幕前的你,有興趣幫助完成,那也歡迎聯系我。不管是互相學習交流,還是互相幫助提升,羅孚都願意,畢竟羅孚也是熱心腸嘛[憨笑]。
福利時間
本文太啰嗦,一篇技術文章寫成了記敘文,羅孚自身也在感慨,看來以后可以考慮做成PPT或視頻的形式,要么更簡單明了,要么更詳細清晰。
本文標題和開篇均提到了源碼和數據,如果是懂技術的你,應該是不屑的,或者Ctrl+U一下就拿到了(加密版本)。如果確實需要下載,可以在“羅孚傳說”公眾號上回復“疫情場所地圖”即可(未加密版本)。如果需要帶詳細注釋的文件來研究,請加入羅孚的知識星球來取(帶注釋版本),當然還可以附送超詳細瓦片地圖離線文件哦,后續也有更多內容分享。
好了,用地圖API制作一份疫情場所分布地圖,今天,你會了嗎?歡迎聯系羅孚,歡迎關注羅孚的公眾號(羅孚傳說)做更多交流,更歡迎加入羅孚的知識星球贊助羅孚獲得更多有價值內容,我們下一篇文章中見。
