空汙查詢網站製作 ajax&google api

#googleAPI

#XHR

#navigator.geolocation

作品連結可點擊首頁右上方查看。

*功能說明:一進入網站詢問是否開啟定位功能。使用定位功能測得最近測站並顯示其AQI、PM2.5、發佈時間以及狀態。下方的地圖將資料視覺化,一覽全臺空汙狀況,點擊可顯示測站名。

1.使用到的變數們

var data;
var nowLongitude,nowLatitude,increaseNum=1000000;
var everyDistance = [],minDistance=10000000,minKey;
var nearSite = document.getElementById("nearSite"),
    aqi=document.getElementById("aqi"),
    pm=document.getElementById("pm"),
    time=document.getElementById("time"),
    airstatus=document.getElementById("airstatus");
var searchSite = document.getElementById("searchSite"),
    confirm = document.getElementById("confirm");
    searchResult = document.getElementById("searchResult");
var url="https://opendata.epa.gov.tw/api/v1/AQI?%24skip=0&%24top=1000&%24format=json";
var mapmarkerLatLng = [],marker=[],infowindow=[],markercolor=[];

2..ajax

首先要取得空汙資料。使用的是政府開放平台的資料(https://opendata.epa.gov.tw/Data/Contents/AQI/),每一小時會更新一次。這份api裡面包含很多資料,有測站名字、縣市、發佈時間、AQI、測站經緯度等。十分詳盡且方便使用。

        var xhr = new XMLHttpRequest();
        xhr.open('GET',url,true);
        xhr.send();
        if(xhr.readyState!=4){
            nearSite.innerHTML="目前無法連上API";
        }
        xhr.onload = function(){
            data = JSON.parse(xhr.response);
            getLocation();
        }

首先建立一個XMLHttpRequest物件,接著open()方法設定('傳送資料的方法'、'資料來源'、'是否為非同步'),而sned()方法送出Http Request,因為沒有要傳送資料過去,所以裡面是null。

xhr.readyState=4時代表連接完成,因此若不等於4時,告訴使用者目前API的連接出現了狀況。xhr.onload代表一連接上的時候執行function。xhr.response為回傳過來的資料,將它轉為JSON格式並定義為data(先看一下data裡面有哪些可以用的資料)。接著執行getLocation()函式。

3.navigator.geolocation

        function getLocation()
        {
            if (navigator.geolocation)
            {
                navigator.geolocation.getCurrentPosition(showPosition);
            }
            else
            {
                alert("瀏覽器不支援");
            }
        }

先判斷navigator.geolocation物件是否可用。如果可以,使用getCurrentPosition(),這個函式會請求使用者的的位置,在()裡加入取得後執行的函式。

        function showPosition(position)
        {
            nowLatitude = position.coords.latitude;
            nowLongitude = position.coords.longitude;
            let a=[],b=[];
            for(let i =0;i<data.length;i++){
                a[i]=(nowLongitude-data[i].Longitude)*(nowLongitude-data[i].Longitude);
                b[i]=(nowLatitude-data[i].Latitude)*(nowLatitude-data[i].Latitude);
                everyDistance[i]=(a[i]+b[i])*increaseNum;
                if(everyDistance[i]<minDistance){
                    minDistance=everyDistance[i];
                    minKey=i;
                }
            }
            nearSite.innerHTMLdata[minKey].SiteName;
            searchSite.innerHTMLdata[minKey].SiteName;
            aqi.innerHTMLdata[minKey]['AQI'];
            pm.innerHTMLdata[minKey]['PM2.5'];
            time.innerHTMLdata[minKey].PublishTime;
            airstatus.innerHTMLdata[minKey].Status;
            addmarker();
            search();
        }

showPosition裡面做的事情是利用取得的使用者位置跟xhr要來的資料,判斷最近的測站,以及在網頁上顯示該測站測得的資料。

position.coords.latitude、position.coords.longitude 這兩個即為使用者同意後,得到的位置經緯度。定義在nowLatitude跟nowLongitude裡面。接著開始計算每個測站跟這個經緯度之間的距離,用距離公式:((x1-x2)平方+(y1-y2)平方)開根號。這邊因為只是比較距離,就不開根號了。

先將每筆算出來的距離存進everyDistance[]陣列裡面,這邊乘上了increasenum變數是因為相減之後會得到小數點後很多位,為避免得到一堆零無法比較結果的情況,調整位數。

在計算距離、距離塞入陣列的時候,開始取得最小值。minDistance一開始先定為一個很大的數字,接著去跟每一個距離做比較,如果資料裡的某一筆值小於minDistance,則minDistance賦值為此筆資料。以此下去找到最小值。再將篩選出的資料丟到html上。

4.搜尋測站資料

        function search(){
            var optionGroup="";
            for(let i =0;i<data.length;i++){
                optionGroup += '<option value="'+i+'">'+data[i].SiteName+'</option>';
            }
            searchResult.innerHTML = optionGroup;
        }
        confirm.addEventListener('click',function(){
            searchSite.innerHTMLdata[searchResult.value].SiteName;
            aqi.innerHTMLdata[searchResult.value]['AQI'];
            pm.innerHTMLdata[searchResult.value]['PM2.5'];
            time.innerHTMLdata[searchResult.value].PublishTime;
            airstatus.innerHTMLdata[searchResult.value].Status;
        });

在html上已經有了<select>,接著要將選項丟入標籤裡面。當確認按鈕(這邊命名為confirm)點下去後,serchResult是select標籤上的id。運用上面有將測站於data中的key設為value,取得要顯示那些資料。

5.google api

google api的部分添加marker跟infowindow。marker的部分,會添加在各個測站的位置上,並以圓形顯示,AQI數值越大、透明度越大、scale越大,並且以該狀態代表的顏色顯示;infowindow則單純顯示測站名字。

(顏色參考:https://airtw.epa.gov.tw/CHT/Information/Standard/AirQualityIndicator.aspx)

使用google api 需先在script引入。之後在html添加放地圖的區塊,一開始的時候先渲染出地圖。

        function initialize(position) {
            map = new google.maps.Map(document.getElementById('map'), {
                zoom: 8,
                center: new google.maps.LatLng(23.5,121),
                mapTypeId: 'terrain'
            });
        }

創造出google.maps.Map物件,第二個參數定義了map的各項設定,讓中心在臺灣島中間。

function addmarker(){
            for(let i =0;i<data.length;i++){
                if0<=data[i].AQI&&data[i].AQI<=50)markercolor[i]="green";
                else if(51<=data[i].AQI&&data[i].AQI<=100)markercolor[i]="yellow";
                else if(101<=data[i].AQI&&data[i].AQI<=150)markercolor[i]="orange";
                else if(151<=data[i].AQI&&data[i].AQI<=200)markercolor[i]="red";
                else if(201<=data[i].AQI&&data[i].AQI<=300)markercolor[i]="purple";
                else if(301<=data[i].AQI&&data[i].AQI<=500)markercolor[i]="brown";
                markerLatLng.push({});
                markerLatLng[i].lat = parseFloat(data[i].Latitude,10);
                markerLatLng[i].lng = parseFloat(data[i].Longitude,10);
                marker[i]= new google.maps.Marker({
                    position:markerLatLng[i],
                    map:map,
                    icon: {
                        path: google.maps.SymbolPath.CIRCLE,
                        fillColor: markercolor[i],
                        fillOpacity: data[i]['AQI']/200,
                        scale:  data[i]['AQI']/5,
                        strokeWeight: .5,
                        strokeColor: 'white'
                    },
                    label:{text:data[i]['AQI'],color:"blue"}
                });
            }

添加marker要創建google.maps.Marker物件,這邊特別處理的是marker的位置、icon、label。

上面的markerLatLng陣列紀錄的是每一個測站的經緯度。在迴圈裡面,每一次執行時先將物件塞入陣列裡面,然後物件裡面寫入每一筆資料的lat、lng值。這邊會這麼做是因為,position的格式要呈現"{lat=.. , lng=...}"。

icon的部分,fillColor填入用markercolor[]記錄下來的每一筆資料的顏色。上面一連串的判斷中,決定了什麼數值配對什麼顏色 ; fillOpacity、scale也用AQI的數值去決定了scale跟透明度。

而infowindow的部分,也寫在一個function裡面

               for(let i =0 ;i<data.length;i++){
                infowindow[i] = new google.maps.InfoWindow({
                    content: ""+data[i].SiteName
                });
                marker[i].addListener('click'function() {
                infowindow[i].open(mapmarker[i]);
            });
    

創建google.maps.InfoWindow物件,內容定為測站名字。加上監聽事件,點擊後,open(map,哪一個marker)

這樣就完成了

以下為demo影片





留言