iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 22
1

練習使用 Geolocation API 取得目前的位置,然後用經緯度去找最近的觀測站。
這邊我用了氣象資料開放平台的 open data。
https://opendata.cwb.gov.tw/dataset/observation/O-A0001-001

準備後端 API

首先可能要去氣象資料開放平台取得授權碼,蠻簡單的,登入之後會員資訊有個 API 授權頁面。
然後就可以再他們的 Swagger 中測試。
https://opendata.cwb.gov.tw/dist/opendata-swagger.html#/%E8%A7%80%E6%B8%AC/get_v1_rest_datastore_O_A0001_001

開始寫我們的 API ,老實說我覺得他們的 Model 有點麻煩很多層。

這是我最後要吐給行動端最終的 Model

    public class WeatherModel
    {
        /// <summary>
        /// 緯度 (座標系統採TWD67),單位 度
        /// </summary>
        public double Lat { get; set; }

        /// <summary>
        /// 經度 (座標系統採TWD67),單位 度
        /// </summary>
        public double Lon { get; set; }

        /// <summary>
        /// 測站名稱
        /// </summary>
        public string LocationName { get; set; }

        /// <summary>
        /// 測站ID
        /// </summary>
        public string StationId { get; set; }

        /// <summary>
        /// 觀測資料時間
        /// </summary>
        public DateTime ObsTime { get; set; }

        /// <summary>
        /// ELE 高度,單位 公尺
        /// </summary>
        public double ELEV { get; set; }

        /// <summary>
        /// WDIR 風向,單位 度,一般風向 0 表示無風
        /// </summary>
        public double WDIR { get; set; }

        /// <summary>
        /// WDSD 風速,單位 公尺/秒
        /// </summary>
        public double WDSD { get; set; }

        /// <summary>
        /// TEMP 溫度,單位 攝氏
        /// </summary>
        public double TEMP { get; set; }

        /// <summary>
        /// HUMD 相對濕度,單位 百分比率,此處以實數 0-1.0 記錄
        /// </summary>
        public double HUMD { get; set; }

        /// <summary>
        /// PRES 測站氣壓,單位 百帕
        /// </summary>
        public double PRES { get; set; }

        /// <summary>
        /// H_24R 日累積雨量,單位 毫米
        /// </summary>
        public double H_24R { get; set; }

        /// <summary>
        /// H_FX 小時最大陣風風速,單位 公尺/秒
        /// </summary>
        public double H_FX { get; set; }

        /// <summary>
        /// H_XD 小時最大陣風風向,單位 度
        /// </summary>
        public object H_XD { get; set; }

        /// <summary>
        /// H_FXT 小時最大陣風時間,yyyy-MM-ddThh:mm:ss+08:00
        /// </summary>
        public object H_FXT { get; set; }

        /// <summary>
        /// D_TX 本日最高溫,單位 攝氏
        /// </summary>
        public double D_TX { get; set; }

        /// <summary>
        /// D_TXT 本日最高溫發生時間,hhmm (小時分鐘)
        /// </summary>
        public object D_TXT { get; set; }

        /// <summary>
        /// D_TN 本日最低溫,單位 攝氏
        /// </summary>
        public double D_TN { get; set; }


        /// <summary>
        /// D_TNT 本日最低溫發生時間,hhmm (小時分鐘)
        /// </summary>
        public object D_TNT { get; set; }

        /// <summary>
        /// CITY 縣市
        /// </summary>
        public string CITY { get; set; }

        /// <summary>
        /// CITY_SN 縣市編號
        /// </summary>
        public string CITY_SN { get; set; }

        /// <summary>
        /// TOWN 鄉鎮
        /// </summary>
        public string TOWN { get; set; }

        /// <summary>
        /// TOWN_SN 鄉鎮編號
        /// </summary>
        public string TOWN_SN { get; set; }

    }

以下都是為了 json to C# 用的

    public class WeatherDataModel
    {
        public bool success { get; set; }

        public WeatherDataResult result { get; set; }

        public WeatherDataRecords records { get; set; }
    }

    public class WeatherDataResult
    {
        public string resource_id { get; set; }

        public List<WeatherDataResultFields> fields { get; set; }
    }

    public class WeatherDataResultFields
    {
        public string id { get; set; }
        public string type { get; set; }
    }

    public class WeatherDataRecords
    {
        public List<WeatherDataRecordLocation> location { get; set; }
    }

    public class WeatherDataRecordLocation
    {
        public double lat { get; set; }
        public double lon { get; set; }
        public string locationName { get; set; }
        public string stationId { get; set; }
        public WeatherDataRecordLocationTime time { get; set; }
        public List<WeatherDataRecordLocationWeatherelement> weatherElement { get; set; }
        public List<WeatherDataRecordLocationParameter> parameter { get; set; }
    }

    public class WeatherDataRecordLocationTime
    {
        public DateTime obsTime { get; set; }
    }

    public class WeatherDataRecordLocationWeatherelement
    {
        public string elementName { get; set; }
        public object elementValue { get; set; }
    }

    public class WeatherDataRecordLocationParameter
    {
        public string parameterName { get; set; }
        public string parameterValue { get; set; }
    }

API

        [HttpGet]
        [Route("Weather")]
        public async Task<IHttpActionResult> GetWeatherStepAsync(double lat, double lon)
        {
            var key = System.Configuration.ConfigurationManager.AppSettings["WeatherKey"];
            var url = $"https://opendata.cwb.gov.tw/api/v1/rest/datastore/O-A0001-001?Authorization={ key }";

            HttpClient httpClient = new HttpClient();
            HttpResponseMessage response = await httpClient.GetAsync(url);
            response.EnsureSuccessStatusCode();
            string body = await response.Content.ReadAsStringAsync();
            WeatherDataModel data = JsonConvert.DeserializeObject<WeatherDataModel>(body);

            var result = data.records.location
                .OrderBy(l => Math.Pow(lat - l.lat, 2) + Math.Pow(lon - l.lon, 2))
                .FirstOrDefault();

            var obj = new WeatherModel();

            obj.Lat = result.lat;
            obj.Lon = result.lon;
            obj.LocationName = result.locationName;
            obj.StationId = result.stationId;
            obj.ObsTime = result.time.obsTime;
            obj.ELEV = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "ELEV").elementValue);
            obj.WDIR = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "WDIR").elementValue);
            obj.WDSD = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "WDSD").elementValue);
            obj.TEMP = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "TEMP").elementValue);
            obj.HUMD = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "HUMD").elementValue);
            obj.PRES = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "PRES").elementValue);
            obj.H_24R = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "H_24R").elementValue);
            obj.H_FX = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "H_FX").elementValue);
            obj.H_XD = result.weatherElement.FirstOrDefault(e => e.elementName == "H_XD").elementValue;
            obj.H_FXT = result.weatherElement.FirstOrDefault(e => e.elementName == "H_FXT").elementValue;
            obj.D_TX = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "D_TX").elementValue);
            obj.D_TXT = result.weatherElement.FirstOrDefault(e => e.elementName == "D_TXT").elementValue;
            obj.D_TN = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "D_TN").elementValue);
            obj.D_TNT = result.weatherElement.FirstOrDefault(e => e.elementName == "D_TNT").elementValue;
            obj.CITY = result.parameter.FirstOrDefault(p => p.parameterName == "CITY").parameterValue;
            obj.CITY_SN = result.parameter.FirstOrDefault(p => p.parameterName == "CITY_SN").parameterValue;
            obj.TOWN = result.parameter.FirstOrDefault(p => p.parameterName == "TOWN").parameterValue;
            obj.TOWN_SN = result.parameter.FirstOrDefault(p => p.parameterName == "TOWN_SN").parameterValue;

            return Ok(obj);
        }

postman 測試一下 API

Weather 用手機位置取得最近觀測站的資訊

用來接 API 資料的 Model
weather-model.ts

export class WeatherModel {
  Lat: number;
  Lon: number;
  LocationName: string;
  StationId: string;
  ObsTime: Date;
  ELEV: number;
  WDIR: number;
  WDSD: number;
  TEMP: number;
  HUMD: number;
  PRES: number;
  H_24R: number;
  H_FX: number;
  H_XD: any;
  H_FXT: any;
  D_TX: number;
  D_TXT: any;
  D_TN: number;
  D_TNT: any;
  CITY: string;
  CITY_SN: string;
  TOWN: string;
  TOWN_SN: string;
}

weather.page.html

<ion-header>
  <ion-toolbar>
    <ion-buttons slot="start">
      <ion-back-button defaultHref="/"></ion-back-button>
    </ion-buttons>
    <ion-title>weather</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <ion-grid>
    <ion-row>
      <ion-col>
        最近觀測站 - 天氣資訊
      </ion-col>
    </ion-row>
    <ion-row>
      <ion-col>觀測站緯度</ion-col>
      <ion-col>
        {{ model?.Lat }}
      </ion-col>
    </ion-row>
    <ion-row>
      <ion-col>觀測站經度</ion-col>
      <ion-col> {{ model?.Lon }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>測站ID</ion-col>
      <ion-col> {{ model?.StationId }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>觀測資料時間</ion-col>
      <ion-col> {{ model?.ObsTime }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>高度</ion-col>
      <ion-col> {{ model?.ELEV }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>風向</ion-col>
      <ion-col> {{ model?.WDIR }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>風速</ion-col>
      <ion-col> {{ model?.WDSD }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>溫度</ion-col>
      <ion-col> {{ model?.TEMP }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>相對濕度</ion-col>
      <ion-col> {{ model?.HUMD }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>測站氣壓</ion-col>
      <ion-col> {{ model?.PRES }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>日累積雨量</ion-col>
      <ion-col> {{ model?.H_24R }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>小時最大陣風風速</ion-col>
      <ion-col> {{ model?.H_FX }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>小時最大陣風風向</ion-col>
      <ion-col> {{ model?.H_XD }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>小時最大陣風時間</ion-col>
      <ion-col> {{ model?.H_FXT }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>本日最高溫</ion-col>
      <ion-col> {{ model?.D_TX }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>本日最高溫發生時間</ion-col>
      <ion-col> {{ model?.D_TXT }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>本日最低溫</ion-col>
      <ion-col> {{ model?.D_TN }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>本日最低溫發生時間</ion-col>
      <ion-col> {{ model?.D_TNT }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>縣市</ion-col>
      <ion-col> {{ model?.CITY }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>縣市編號</ion-col>
      <ion-col> {{ model?.CITY_SN }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>鄉鎮</ion-col>
      <ion-col> {{ model?.TOWN }}</ion-col>
    </ion-row>
    <ion-row>
      <ion-col>鄉鎮編號</ion-col>
      <ion-col> {{ model?.TOWN_SN }}</ion-col>
    </ion-row>
  </ion-grid>
</ion-content>

weather.page.ts

import { HttpClient } from '@angular/common/http';
import { WeatherModel } from './weather-model';
import { Component, OnInit } from '@angular/core';
import { Plugins } from '@capacitor/core';

const { Geolocation } = Plugins;

@Component({
  selector: 'app-weather',
  templateUrl: './weather.page.html',
  styleUrls: ['./weather.page.scss'],
})
export class WeatherPage implements OnInit {
  public model: WeatherModel;
  constructor(private http: HttpClient) {}

  ngOnInit() {
    Geolocation.getCurrentPosition().then((coordinates) => {
      this.weather(coordinates.coords.latitude, coordinates.coords.longitude);
    });
  }
  weather(lat: number, lon: number) {
    this.http
      .get<WeatherModel>('http://192.168.1.102/BES/api/Demo3/Weather', {
        params: {
          lat: lat.toString(),
          lon: lon.toString(),
        },
      })
      .subscribe((result) => {
        this.model = result;
      });
  }
}

最主要就是 Capacitor Geolocation API 的部分

小結語
這篇最麻煩的其實是後端。XDDD

結果


上一篇
Record
下一篇
Device & Network
系列文
純粹沒有寫過行動,Ionic 學習中...30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言