@page "/grumap"
@using System.Xml.Linq
@using Microsoft.Extensions.Configuration
@inject IJSRuntime JSRuntime
@inject IConfiguration Config
<div style="display:flex;justify-content:center;align-items:center;flex-direction:column">
<h2 style="text-align:center">グルメマップもどき</h2>
<div style="padding-right:2em;width:100%;text-align:right"><span style="font-size:x-small;font-weight:bold">Presented by Sumomo Lab.</span></div>
<div style="padding-right:2em;width:100%;display:flex;justify-content:right"><a href="http://webservice.recruit.co.jp/"><img src="http://webservice.recruit.co.jp/banner/hotpepper-s.gif" alt="ホットペッパー Webサービス" width="135" height="17" border="0" title="ホットペッパー Webサービス"></a></div>
</div>
<br/>
<a href="ShowCode?FileName=Grumap.razor.src&Return=grumap">本体ソース</a>
<br/>
<!-- MAPエリア -->
<div id="myMap" style="width:100%;height:600px">
</div>
<br/>
<hr/>
<!-- 検索範囲 変数をbindすることにより、相互に連動する -->
検索範囲:<select @bind="range">
<option value="1">300m</option>
<option value="2">500m</option>
<option value="3">1km</option>
<option value="4">2km</option>
<option value="5">5km</option>
</select>
<button class="btn btn-primary" @onclick="btnRtv_Clicked">検索</button>
<br/>
検索種別:<br/>
@if (Genres != null) { // ジャンル取得済みなら、ジャンル一覧を表示
int cnt = 0;
@foreach(var g in Genres) {
if (cnt != 0 && (cnt % 5) == 0) {
<br/>
}
<!-- 変数をbindすることにより、List中の項目の指定プロパティと連動させる -->
<label style="width:200px;"><input type="checkbox" @bind="g.Checked"/>@g.Name</label>
cnt++;
}
<br/>
<span>緯度:</span><span>@Latitude</span><span>,経度:</span><span>@Longitude</span>
<br/>
<pre>@Message</pre>
}
@code{
// Viewとの共有変数
protected List<Genre> Genres; // ジャンル一覧
protected int range = 3; // 検索範囲
protected string Message; // メッセージ
protected double Latitude; // 緯度
protected double Longitude; // 経度
// ローカル
private string HpAPIBaseUrl = ""; // Hotpepper Web APIのベースURL
private string APIKey = ""; // HotPepper Service ID
private HttpClient htcli;
/// <summary>
/// 初期化完了時処理
/// </summary>
/// <returns></returns>
protected override async Task OnInitializedAsync() {
htcli = new HttpClient();
// configファイルからAPIアクセス情報を取得
APIKey = Config.GetSection("HotpepperAPI").GetValue<string>("APIKey");
HpAPIBaseUrl = Config.GetSection("HotpepperAPI").GetValue<string>("BaseUrl");
// ジャンル一覧をWeb APIで取得
Genres = new List<Genre>();
var xmlstr = await htcli.GetStringAsync($"{HpAPIBaseUrl}genre/v1/?key={APIKey}");
XDocument doc = XDocument.Parse(xmlstr);
XNamespace ns = "http://webservice.recruit.co.jp/HotPepper/";
foreach(var ele in doc!.Root!.Elements(ns+"genre")) {
Genres.Add(new Genre() {
Code = ele.Element(ns+"code")!.Value.Replace("\n","").Replace("\r","").Trim(),
Name = ele.Element(ns+"name")!.Value.Replace("\n","").Replace("\r","").Trim()
});
}
}
/// <summary>
/// 画面表示完了時処理
/// Javascriptを呼び出すので、こちらで実行
/// </summary>
/// <param name="firstRender">最初の呼び出しか</param>
/// <returns></returns>
protected override async Task OnAfterRenderAsync(bool firstRender) {
if (firstRender) {
// MAPの初期化
await JSRuntime.InvokeVoidAsync("BingMapLib.Functions.loadMap","myMap");
// MAPクリック時の処理を設定(C#コード呼び出し)
await JSRuntime.InvokeVoidAsync("BingMapLib.Functions.AttachEvents",
"click",
DotNetObjectReference.Create(this),
"OnMapClick"
);
// 現在地の取得
await JSRuntime.InvokeVoidAsync("BingMapLib.Functions.GetCurrentPosition",
DotNetObjectReference.Create(this),
"GetLocationCompleted",
"GetLocationFailed"
);
}
}
/// <summary>
/// MAPクリック時の処理
/// Javascriptから呼び出される
/// </summary>
/// <param name="latitude">緯度</param>
/// <param name="longitude">経度</param>
/// <returns></returns>
[JSInvokable]
public async Task OnMapClick(double latitude, double longitude) {
// 緯度経度のC#コード側への保存
Latitude = latitude;
Longitude = longitude;
// Pushpinの追加
await JSRuntime.InvokeVoidAsync("BingMapLib.Functions.AddPushpin",latitude,longitude,"検索開始位置","#8080ff");
this.StateHasChanged();
}
/// <summary>
/// 検索ボタンクリック時処理
/// </summary>
/// <returns></returns>
protected async Task btnRtv_Clicked() {
if (Latitude == 0) {
Message = "検索開始位置が指定されていません。";
return;
}
// URLの作成
string url = $"{HpAPIBaseUrl}gourmet/v1/?key={APIKey}&lat={Latitude}&lng={Longitude}&range={range}&count=20";
// チェックされたジャンルをパラメータに追加
foreach(var g in Genres!) {
if (g.Checked) {
url += $"&genre={g.Code}";
}
}
// Web APIを使用して、周辺の店舗を取得する
string xml = await htcli.GetStringAsync(url);
XDocument doc = XDocument.Parse(xml);
XNamespace ns = "http://webservice.recruit.co.jp/HotPepper/";
List<ShopInfo> Shops = new List<ShopInfo>();
// 取得したXMLから情報をセット
foreach(var ele in doc.Root!.Elements(ns+"shop")) {
ShopInfo shop = new ShopInfo() {
Name = ele.Element(ns+"name")!.Value.Replace("\n","").Replace("\r","").Trim(),
Genre = ele.Element(ns+"genre")!.Element(ns+"name")!.Value.Replace("\n","").Replace("\r","").Trim(),
OpenTime = ele.Element(ns+"open")!.Value.Replace("\n","").Replace("\r","").Trim(),
Latitude = Convert.ToDouble(ele.Element(ns+"lat")!.Value.Replace("\n","").Replace("\r","").Trim()),
Longitude = Convert.ToDouble(ele.Element(ns+"lng")!.Value.Replace("\n","").Replace("\r","").Trim()),
Url = ele.Element(ns+"urls")!.Element(ns+"pc")!.Value.Replace("\r","").Trim()
};
Shops.Add(shop);
}
// 店舗マーカーをMAP上に表示
await JSRuntime.InvokeVoidAsync("BingMapLib.Functions.ClearRestrans");
int cnt = 1;
foreach(var itm in Shops) {
await JSRuntime.InvokeVoidAsync("BingMapLib.Functions.AddRestran",
new {
latitude = itm.Latitude,
longitude = itm.Longitude,
color = "#ff0cc0",
name = itm.Name,
number = cnt.ToString(),
desc = $"営業時間:{itm.OpenTime}<br/>URL:<a href='{itm.Url}' target='_blank'>店舗ホームページ</a>"
}
);
cnt++;
}
this.StateHasChanged();
}
/// <summary>
/// 現在地取得完了
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
[JSInvokable]
public async Task GetLocationCompleted(GeoInfo e) {
await JSRuntime.InvokeVoidAsync("BingMapLib.Functions.SetMapCenter",e.Coords!.Latitude,e.Coords!.Longitude);
this.StateHasChanged();
}
/// <summary>
/// 現在地取得失敗
/// </summary>
/// <param name="e"></param>
[JSInvokable]
public void GetLocationFailed(GeoError e) {
Message = $"現在地取得エラー:{e.Code}:{e.Message}";
this.StateHasChanged();
}
/// <summary>
/// ジャンル情報
/// </summary>
public class Genre {
/// <summary>
/// ジャンルがチェックされているか(画面連動用)
/// </summary>
/// <value>チェック状態</value>
public bool Checked { get; set; }
/// <summary>
/// ジャンルコード
/// </summary>
/// <value>ジャンルコード</value>
public string Code { get; set; }
/// <summary>
/// ジャンル名
/// </summary>
/// <value>ジャンル名</value>
public string Name { get; set; }
}
/// <summary>
/// 店舗情報
/// </summary>
public class ShopInfo {
/// <summary>
/// 店舗名
/// </summary>
/// <value>店舗名</value>
public string Name { get; set; }
/// <summary>
/// ジャンル名
/// </summary>
/// <value>ジャンル名</value>
public string Genre {get; set; }
/// <summary>
/// 営業時間
/// </summary>
/// <value>営業時間</value>
public string OpenTime { get; set; }
/// <summary>
/// 店舗ホームページURL
/// </summary>
/// <value>URL</value>
public string Url { get; set; }
/// <summary>
/// 緯度
/// </summary>
/// <value>緯度</value>
public double Latitude { get; set;}
/// <summary>
/// 経度
/// </summary>
/// <value>経度</value>
public double Longitude { get; set; }
}
// 位置情報
public class GeoInfo {
public Coords Coords { get; set; }
public long Timestamp { get; set; }
}
public class Coords {
public double Latitude {get; set;} // 緯度
public double Longitude {get; set;} // 経度
}
// エラー情報
public class GeoError {
public int Code { get; set; }
public string Message { get; set; }
}
}