チュートリアル15

提供: svg2wiki
(版間での差分)
移動: 案内, 検索
(ページの作成:「= チュートリアル15 WebApp Layer 伸縮スクロールに応じたベクトル地理情報サービス結合 = 動的にベクトルデータが生成・配信さ...」)
 
(CanadianGeoNames.html)
 
(1人の利用者による、間の29版が非表示)
1行: 1行:
 
= チュートリアル15 WebApp Layer 伸縮スクロールに応じたベクトル地理情報サービス結合 =
 
= チュートリアル15 WebApp Layer 伸縮スクロールに応じたベクトル地理情報サービス結合 =
動的にベクトルデータが生成・配信されているサービスをSVGMap.jsに結合します。[[チュートリアル14]]に対して、こちらは伸縮スクロールする度にその表示領域に応じたデータをサービスから取得して表示します。
+
動的にベクトルデータが生成・配信されているサービスをSVGMap.jsに結合します。[[チュートリアル14]]に対して、こちらは伸縮スクロールする度にその表示領域に応じたデータをサービスから取得して表示します。また[[チュートリアル14]]はgeoJsonデータのサービスでしたがこちらはCSVデータです。
  
 
結合するサービスはNatural Resources Canadaが提供している、[https://www.nrcan.gc.ca/maps-tools-and-publications/maps/geographical-names-canada/application-programming-interface-api/9249 Geoname Service API](カナダの地名データサービス)です。
 
結合するサービスはNatural Resources Canadaが提供している、[https://www.nrcan.gc.ca/maps-tools-and-publications/maps/geographical-names-canada/application-programming-interface-api/9249 Geoname Service API](カナダの地名データサービス)です。
  
* 実際の動作は、[https://svgmap.org/devinfo/devkddi/tutorials/geojson2/geojson2.html こちら]をクリック。
+
* 実際の動作は、[https://svgmap.org/devinfo/devkddi/tutorials/vectorService1/vectorService1.html こちら]をクリック。
* [https://svgmap.org/devinfo/devkddi/tutorials/geojson2/ ソースコードのディレクトリ]
+
* 使用ファイルの[https://www.svgmap.org/devinfo/devkddi/tutorials/vectorService1.zip ZIPアーカイブファイル]
  
== geojson1.html ==
+
 
* [[チュートリアル6]]と特に違いはありません。
+
== vectorService1.html ==
 +
* [[チュートリアル14]]と特に違いはありません。
  
 
== Container.svg ==
 
== Container.svg ==
* [[チュートリアル6]]と特に違いはありません。
+
* [[チュートリアル14]]と特に違いはありません。
 
+
==geoJsonExample2.svg==
+
* ドキュメントルート要素(svg要素)の、data-controller属性で、この[[解説書#.E3.83.AC.E3.82.A4.E3.83.A4.E3.83.BC.E5.9B.BA.E6.9C.89.E3.81.AEUI|レイヤーを操作するwebApp]]を指定しています。
+
**<code>data-controller="geoJsonExample1.html#exec=appearOnLayerLoad</code>
+
**<code>exec=appearOnLayerLoad</code>は、レイヤが表示状態になるとwebAppのウィンドが出現する設定です。([[解説書#.E8.A9.B3.E7.B4.B0|詳しくはこちら]])
+
*defs要素でマーカー(POIのアイコン)を定義しています
+
**マーカーの色はマグニチュードに応じて変化させるためここでは未定義にしてあります
+
  
 +
==CanadianGeoNames.svg==
 +
* [[チュートリアル14]]と特に違いはありません。アイコンの色もこちらは赤に固定しています。
 
<pre>
 
<pre>
 
<?xml version="1.0" encoding="UTF-8"?>
 
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:go="http://purl.org/svgmap/profile" viewBox="12375.0 -4500.0 2250.0 2250.0" go:dataArea="12375.0 -4500.0 2250.0 2250.0" data-controller="geoJsonExample2.html#exec=appearOnLayerLoad" property="name,address,phone,url">
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="120,-50,30,30" data-controller="CanadianGeoNames.html#exec=appearOnLayerLoad">
 
+
<globalCoordinateSystem srsName="http://purl.org/crs/84" transform="matrix(1,0,0,-1,0,0)"/>
<defs>
+
<defs>
<g id="p0">
+
<g id="p0">
<circle cx="0" cy="0" r="10" stroke="none"/>
+
  <circle cx="0" cy="0" r="6" fill="red"/>
</g>
+
</g>
</defs>
+
</defs>
 
+
<globalCoordinateSystem srsName="http://purl.org/crs/84" transform="matrix(100.0,0.0,0.0,-100.0,0.0,0.0)" />
+
<g id="mapContents"></g>
+
 
</svg>
 
</svg>
 
</pre>
 
</pre>
  
==geoJsonExample2.html==
+
==CanadianGeoNames.html, CanadianGeoNames.js==
*geoJsonExample2.svgに紐付けられ、[[解説書#.E3.83.AC.E3.82.A4.E3.83.A4.E3.83.BC.E5.9B.BA.E6.9C.89.E3.81.AEUI|そのDOMをコントロールできるwebApp]]
+
===REST API===
*[[チュートリアル6#geoJsonExample1.html]]に対して以下が相違点
+
少し複雑ですので、[https://www.nrcan.gc.ca/maps-tools-and-publications/maps/geographical-names-canada/application-programming-interface-api/9249 Geographical names in Canada : Geoname Service]が提供するAPIのうち今回使用する部分をまとめます。
*<code>addEventListener("load", function(){..})</code>
+
 
** <code>changeData()</code> UIの設定に基づき、地震データをリクエストして可視化する関数
+
====使用するクエリパラメータ====
*** <code>getUSGSURL()</code> USGSが配信する地震データを取得するためのGETリクエストを生成
+
今回は以下の二つを使います。
*** <code>loadAndDrawGeoJson()</code>
+
 
****<code>buildSchema()</code>
+
* bbox 西、南、東、北の座標(世界測地系の度の値)をカンマ区切りで指定
*****[[解説書#drawGeoJson|svgMapGIStool.drawGeoJson]]関数で可視化する際に、[[解説書#metadata.E3.83.95.E3.83.AC.E3.83.BC.E3.83.A0.E3.83.AF.E3.83.BC.E3.82.AF|SVGMap.jsが持つメタデータ表示フレームワーク]]に適応させるためのスキーマデータを構築
+
* num 出力する最大数
*****svgMapGIStool.drawGeoJson関数で渡す末尾の引数(metaSchema)を生成している
+
 
****<code>setMagColors()</code>
+
* .csv拡張子 厳密にはクエリパラメータではありませんが、パス部の拡張子を設定することで指定したメディアが配信されます。(デフォルトはhtml)今回はCSVを使うことにします。
*****[[解説書#drawGeoJson|svgMapGIStool.drawGeoJson]]関数の持つ、各フィーチャーのproperties値を使ってスタイルを設定可能な機能を使い、マグニチュード値をもとにpointフィーチャの色を指定
+
 
** <code>setInterval(function(){..}..)</code> 指定した間隔で定期的に更新する関数(地震データはリアルタイムに更新されるため)
+
*クロスオリジン設定 今回は別ドメインのサービスにアクセスすることになるので、ウェブサービスがクロスオリジンアクセスを許可している必要があります。[https://www.nrcan.gc.ca/maps-tools-and-publications/maps/geographical-names-canada/application-programming-interface-api/9249 Geographical names in Canada : Geoname Service]は許可されているようです。
 +
 
 +
====配信されるデータ====
 +
クエリパラメータに基づいたCSVデータが返信されます。1行目がスキーマ行2行目以降にデータ続きます。以下のカラムがあるようです。latitude、longitudeカラムで地図上にPointフィーチャを設置、nameカラムでそのタイトルを設置できそうです。その他のカラムはシンボルをクリックしたときにプロパティとして表示できるでしょう。データを取得してみると、カンマを含むデータがダブルクォーテーションでエスケープされていることがあるようです。
 +
*id
 +
*name
 +
*language.code
 +
*language.href
 +
*syllabic
 +
*feature.id
 +
*feature.href
 +
*category
 +
*status.code
 +
*status.href
 +
*concise.code
 +
*concise.href
 +
*generic.code
 +
*generic.href
 +
*location
 +
*province.code
 +
*province.href
 +
*map
 +
*relevance
 +
*accuracy
 +
*latitude
 +
*longitude
 +
*decision
 +
 
 +
===コード===
 +
 
 +
*<code>addEventListener("zoomPanMap", getGeoNames)</code> : 伸縮スクロールの度にサービスに問い合わせ、紐づいたレイヤーのSVGMapコンテンツのDOMを構築し可視化します。
 +
**1ステップ前(伸縮スクロール前)のデータは、たとえ現ステップでも存在していたとしても単純に全消去してから再配置しています。(タイリングなどの、より高度な仕組みは実装していない)
 +
*<code>getGeoNames()</code> : サービスにと合わせて可視化する非同期関数
 +
**<code>svgMap.getGeoViewBox()</code> : 地理的な表示領域を得る
 +
**<code>getCanadianGeoNamesReq()</code> : 表示領域をもとにサービスへのクエリを組み立て
 +
**<code>await getCsv()</code> : クエリを使って非同期でCSVを取得・パース
 +
***<code>line.split(...)</code> : [https://www.ipentec.com/document/csharp-read-csv-file-by-regex こちらの記事]をもとにダブルクォーテーションエスケープを加味してパース
 +
**<code>drawPoints()</code> : 取得したデータを可視化します。 今回はCSVから直接SVGのuse要素を作り、可視化しています。
 +
***<code>svgImage</code> : レイヤーに紐づいたwebAppに組み込まれた同レイヤーのSVGMapドキュメントオブジェクト
 +
****参考:[[解説書#svgImage]]
 +
***<code>schema</code> : 紐づいたレイヤーのSVGMapコンテンツのドキュメント要素のpropertyにスキーマを設置
 +
****参考:[[解説書#metadata.E3.83.95.E3.83.AC.E3.83.BC.E3.83.A0.E3.83.AF.E3.83.BC.E3.82.AF|SVGMap.jsのmetadataフレームワーク]]
 +
***<code>"transform", `ref(svg,${lng},${-lat})`</code> : svg1.2の[https://www.w3.org/TR/SVGTiny12/single-page.html#coords-transform-ref TransformRef]を使い、サイズが変化しないアイコンを設置しています。
 +
****参考:[[解説書#.E5.B1.9E.E6.80.A7|サポートされている属性]]
 +
***<code>"content"</code> : メタデータをcsvで設置
 +
***<code>"xlink:href", "#p0"</code> : defs要素内のid:p0のシンボル(赤い丸)を参照
 +
***<code>svgMap.refreshScreen();</code> : SVGMapコンテンツのDOM生成完了したら再描画する
 +
****参考: [[解説書#.E5.86.8D.E6.8F.8F.E7.94.BB.E3.81.AE.E5.88.B6.E9.99.90|再描画の制限]]
  
 +
CanadianGeoNames.html
 
<pre>
 
<pre>
 
<!doctype html>
 
<!doctype html>
 
<html>
 
<html>
 
<head>
 
<head>
<meta charset="utf-8"/>
+
<title>basic dynamic wms layer controller</title>
<title>SVGMapのwebAppレイヤーで、geoJsonを描画するサンプル</title>
+
<meta charset="utf-8"></meta>
 
</head>
 
</head>
 +
<script src="CanadianGeoNames.js"></script>
 +
<body>
 +
<h3>Canadian GeoNames layer controller</h3>
 +
<p>Get CanadianGeoNames Features from <a href="https://www.nrcan.gc.ca/maps-tools-and-publications/maps/geographical-names-canada/application-programming-interface-api/9249" target="_blank">Canadian GeoNames Search Service</a></p>
 +
<div id="messageDiv" style="color:red">-</div>
 +
</body>
 +
</html>
 +
</pre>
  
<script>
+
CanadianGeoNames.js
var usgsEarthquakeService="https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/";
+
<pre>
var timeSpanKeys=["hour","day","week","month"]; // 配信データの期間設定の選択枝
+
var timeSpanDefault=2; // 過去1週間のデータの表示をデフォルトに
+
var levelKeys=["significant","4.5","2.5","1.0","all"]; // マグニチュード別の配信データの選択枝
+
var levelDefault=2; // M2.5以上の地震の表示をデフォルトに
+
var intervalMinutes=10; // 10分おきに更新する
+
var metaSchema; // SVGMap.jsの標準で用意されているジオメトリ選択時のメタデータ表示UIの正規化されたスキーマを格納する
+
  
addEventListener("load", function(){
+
var canadianGeoNamesService = "https://geogratis.gc.ca/services/geoname/en/geonames.csv";
buildDataSelect();
+
changeData();
+
setInterval(function(){
+
changeData();
+
messageDiv.innerText=new Date().toLocaleString() + " update";
+
} ,intervalMinutes * 60 * 1000);
+
});
+
  
function changeData(){
+
onload=function(){
var param1 = dataSelect1.selectedIndex;
+
addEventListener("zoomPanMap", getGeoNames);
var param2 = dataSelect2.selectedIndex;
+
getGeoNames();
var path = getUSGSURL(param1,param2);
+
loadAndDrawGeoJson(path);
+
 
}
 
}
  
async function loadAndDrawGeoJson(dataPath){
 
var gjs = await loadJSON(dataPath);
 
buildSchema(gjs.features);
 
setMagColors(gjs.features);
 
console.log("geoJson:",gjs);
 
var parentElm = svgImage.getElementById("mapContents");
 
removeChildren(parentElm);
 
svgMapGIStool.drawGeoJson(gjs, layerID, "orange", 2, "orange", "p0", "poi", "", parentElm, metaSchema);
 
svgMap.refreshScreen();
 
}
 
  
function buildDataSelect(){
+
var crsAD=1;
var first=true;
+
var maxItems=100;
for ( var i = 0 ; i < timeSpanKeys.length; i++){
+
 
var timeSpanKey = timeSpanKeys[i];
+
async function getGeoNames(){
var selectedOpt="";
+
var geoViewBox = svgMap.getGeoViewBox(); // 地理的な表示領域を得る
if ( timeSpanDefault == i){
+
var req = getCanadianGeoNamesReq(geoViewBox); // 表示領域をもとにサービスへのクエリを組み立てる
selectedOpt="selected";
+
var csv = await getCsv(req); // クエリを使って非同期でCSVを取得
}
+
if ( csv.length > maxItems){ // 最大数以上の場合メッセージを出す
dataSelect1.insertAdjacentHTML('beforeend', `<option value="${timeSpanKey}" ${selectedOpt}>${timeSpanKey}</option>`);
+
messageDiv.innerText="Exceeded maximum number. Please zoom in.";
}
+
}else{
for ( var i = 0 ; i < levelKeys.length ; i++){
+
messageDiv.innerText="";
var levelKey = levelKeys[i];
+
var selectedOpt="";
+
if ( levelDefault == i){
+
selectedOpt="selected";
+
}
+
dataSelect2.insertAdjacentHTML('beforeend',  `<option value="${levelKey}" ${selectedOpt}>${levelKey}</option>`);
+
 
}
 
}
 +
drawPoints(csv); // 取得したデータを可視化する
 
}
 
}
  
async function loadJSON(url){
+
function getCanadianGeoNamesReq(geoArea){
var response = await fetch(url+"?time="+new Date().getTime()); // 常に最新のデータを得るには何かダミーのクエリパートを付けるBad Tips..
+
var area_x0=geoArea.x;
// https://stackoverflow.com/questions/37204296/cache-invalidation-using-the-query-string-bad-practice
+
var area_y0=geoArea.y;
// https://stackoverflow.com/questions/9692665/cache-busting-via-params
+
var area_x1=geoArea.x+geoArea.width;
var json = await response.json();
+
var area_y1=geoArea.y+geoArea.height;
return ( json );
+
var ans = `${canadianGeoNamesService}?bbox=${area_x0},${area_y0},${area_x1},${area_y1}&num=${maxItems}`;
 +
return ( ans );
 
}
 
}
  
function removeChildren(element){
+
async function getCsv(url){
while (element.firstChild) element.removeChild(element.firstChild);
+
var response = await fetch(url);  
}
+
var txt = await response.text();
 
+
txt = txt.split("\n");
function getUSGSURL(timeSpan, level){
+
var ans = [];
if (!timeSpanKeys[timeSpan]){return};
+
for ( var line of txt ){
if (!levelKeys[level]){return};
+
// https://www.ipentec.com/document/csharp-read-csv-file-by-regex ダブルクォーテーションエスケープを加味したcsvパース
var ans = `${usgsEarthquakeService}${levelKeys[level]}_${timeSpanKeys[timeSpan]}.geojson`;
+
line = line.split(/,(?=(?:[^"]*"[^"]*")*[^"]*$)/);
console.log("getUSGSURL:",ans);
+
if (line.length > 1){
return (ans);
+
ans.push(line);
}
+
 
+
function buildSchema(features){ // geojsonのfeatureのproprerty名から正規化されたスキーマを生成
+
metaSchema={};
+
for ( var feature of features){ // 一応全データをトレース
+
for ( var propName in feature.properties){
+
if (!metaSchema[propName]){
+
metaSchema[propName]=true;
+
}
+
 
}
 
}
 
}
 
}
metaSchema=Object.keys(metaSchema);
+
return ( ans );
svgImage.documentElement.setAttribute("property",metaSchema.join());
+
 
}
 
}
  
function setMagColors(features){ // [[解説書#drawGeoJson]]のスタイリング仕様を使い、マグニチュードに応じた色を付ける
+
function drawPoints(csv){
for ( var feature of features){
+
removeUses();
var mag = feature.properties.mag;
+
var schema = csv[0].join();
// マグニチュード3...7でクリッピング
+
var latCol=csv[0].indexOf("latitude");
mag = Math.max(3,mag);
+
var lngCol=csv[0].indexOf("longitude");
mag = Math.min(7,mag);
+
svgImage.documentElement.setAttribute("property",schema);
// 色相(hue)に変換し、そこからRGBカラーを生成
+
for ( var i = 1 ; i < csv.length ; i++){
var hue = (7-mag)/(4)*240;
+
var point = csv[i];
var rgb = svgMapGIStool.hsv2rgb(hue,100,100);
+
var meta = point.join();
console.log(rgb);
+
var lat = Number(point[latCol]);
if ( rgb){
+
var lng = Number(point[lngCol]);
feature.properties["marker-color"]=`#${rgb.r.toString(16).padStart(2, '0')}${rgb.g.toString(16).padStart(2, '0')}${rgb.b.toString(16).padStart(2, '0')}`;
+
var use=svgImage.createElement("use");
}
+
use.setAttribute("xlink:href","#p0");
 +
use.setAttribute("content",meta);
 +
use.setAttribute("x",0);
 +
use.setAttribute("y",0);
 +
use.setAttribute("transform",`ref(svg,${lng},${-lat})`);
 +
svgImage.documentElement.appendChild(use);
 
}
 
}
console.log(features);
+
svgMap.refreshScreen();
 
}
 
}
  
</script>
+
function removeUses(){
<body>
+
var uses = svgImage.getElementsByTagName("use");
<h3>area layer</h3>
+
for ( var i = uses.length-1 ; i >=0 ; i--){
<p><a href="https://earthquake.usgs.gov/earthquakes/feed/">USGS Earthquake Hazards Program Feed</a>の可視化</p>
+
uses[i].remove();
期間<select id="dataSelect1" onchange="changeData()"></select><br>
+
}
規模<select id="dataSelect2" onchange="changeData()"></select>
+
}
<div id="messageDiv"></div>
+
</body>
+
</html>
+
 
</pre>
 
</pre>

2024年7月19日 (金) 10:32時点における最新版

目次

[編集] チュートリアル15 WebApp Layer 伸縮スクロールに応じたベクトル地理情報サービス結合

動的にベクトルデータが生成・配信されているサービスをSVGMap.jsに結合します。チュートリアル14に対して、こちらは伸縮スクロールする度にその表示領域に応じたデータをサービスから取得して表示します。またチュートリアル14はgeoJsonデータのサービスでしたがこちらはCSVデータです。

結合するサービスはNatural Resources Canadaが提供している、Geoname Service API(カナダの地名データサービス)です。


[編集] vectorService1.html

[編集] Container.svg

[編集] CanadianGeoNames.svg

  • チュートリアル14と特に違いはありません。アイコンの色もこちらは赤に固定しています。
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="120,-50,30,30" data-controller="CanadianGeoNames.html#exec=appearOnLayerLoad">
 <globalCoordinateSystem srsName="http://purl.org/crs/84" transform="matrix(1,0,0,-1,0,0)"/>
 <defs>
 <g id="p0">
   <circle cx="0" cy="0" r="6" fill="red"/>
 </g>
 </defs>
</svg>

[編集] CanadianGeoNames.html, CanadianGeoNames.js

[編集] REST API

少し複雑ですので、Geographical names in Canada : Geoname Serviceが提供するAPIのうち今回使用する部分をまとめます。

[編集] 使用するクエリパラメータ

今回は以下の二つを使います。

  • bbox 西、南、東、北の座標(世界測地系の度の値)をカンマ区切りで指定
  • num 出力する最大数
  • .csv拡張子 厳密にはクエリパラメータではありませんが、パス部の拡張子を設定することで指定したメディアが配信されます。(デフォルトはhtml)今回はCSVを使うことにします。
  • クロスオリジン設定 今回は別ドメインのサービスにアクセスすることになるので、ウェブサービスがクロスオリジンアクセスを許可している必要があります。Geographical names in Canada : Geoname Serviceは許可されているようです。

[編集] 配信されるデータ

クエリパラメータに基づいたCSVデータが返信されます。1行目がスキーマ行2行目以降にデータ続きます。以下のカラムがあるようです。latitude、longitudeカラムで地図上にPointフィーチャを設置、nameカラムでそのタイトルを設置できそうです。その他のカラムはシンボルをクリックしたときにプロパティとして表示できるでしょう。データを取得してみると、カンマを含むデータがダブルクォーテーションでエスケープされていることがあるようです。

  • id
  • name
  • language.code
  • language.href
  • syllabic
  • feature.id
  • feature.href
  • category
  • status.code
  • status.href
  • concise.code
  • concise.href
  • generic.code
  • generic.href
  • location
  • province.code
  • province.href
  • map
  • relevance
  • accuracy
  • latitude
  • longitude
  • decision

[編集] コード

  • addEventListener("zoomPanMap", getGeoNames) : 伸縮スクロールの度にサービスに問い合わせ、紐づいたレイヤーのSVGMapコンテンツのDOMを構築し可視化します。
    • 1ステップ前(伸縮スクロール前)のデータは、たとえ現ステップでも存在していたとしても単純に全消去してから再配置しています。(タイリングなどの、より高度な仕組みは実装していない)
  • getGeoNames() : サービスにと合わせて可視化する非同期関数
    • svgMap.getGeoViewBox() : 地理的な表示領域を得る
    • getCanadianGeoNamesReq() : 表示領域をもとにサービスへのクエリを組み立て
    • await getCsv() : クエリを使って非同期でCSVを取得・パース
      • line.split(...) : こちらの記事をもとにダブルクォーテーションエスケープを加味してパース
    • drawPoints() : 取得したデータを可視化します。 今回はCSVから直接SVGのuse要素を作り、可視化しています。
      • svgImage : レイヤーに紐づいたwebAppに組み込まれた同レイヤーのSVGMapドキュメントオブジェクト
      • schema : 紐づいたレイヤーのSVGMapコンテンツのドキュメント要素のpropertyにスキーマを設置
      • "transform", `ref(svg,${lng},${-lat})` : svg1.2のTransformRefを使い、サイズが変化しないアイコンを設置しています。
      • "content" : メタデータをcsvで設置
      • "xlink:href", "#p0" : defs要素内のid:p0のシンボル(赤い丸)を参照
      • svgMap.refreshScreen(); : SVGMapコンテンツのDOM生成完了したら再描画する

CanadianGeoNames.html

<!doctype html>
<html>
<head>
<title>basic dynamic wms layer controller</title>
<meta charset="utf-8"></meta>
</head>
<script src="CanadianGeoNames.js"></script>
<body>
<h3>Canadian GeoNames layer controller</h3>
<p>Get CanadianGeoNames Features from <a href="https://www.nrcan.gc.ca/maps-tools-and-publications/maps/geographical-names-canada/application-programming-interface-api/9249" target="_blank">Canadian GeoNames Search Service</a></p>
<div id="messageDiv" style="color:red">-</div>
</body>
</html>

CanadianGeoNames.js


var canadianGeoNamesService = "https://geogratis.gc.ca/services/geoname/en/geonames.csv";

onload=function(){
	addEventListener("zoomPanMap",  getGeoNames);
	getGeoNames();
}


var crsAD=1;
var maxItems=100;

async function getGeoNames(){
	var geoViewBox = svgMap.getGeoViewBox(); // 地理的な表示領域を得る
	var req = getCanadianGeoNamesReq(geoViewBox); // 表示領域をもとにサービスへのクエリを組み立てる
	var csv = await getCsv(req); // クエリを使って非同期でCSVを取得
	if ( csv.length > maxItems){ // 最大数以上の場合メッセージを出す
		messageDiv.innerText="Exceeded maximum number. Please zoom in.";
	}else{
		messageDiv.innerText="";
	}
	drawPoints(csv); // 取得したデータを可視化する
}

function getCanadianGeoNamesReq(geoArea){
	var area_x0=geoArea.x;
	var area_y0=geoArea.y;
	var area_x1=geoArea.x+geoArea.width;
	var area_y1=geoArea.y+geoArea.height;
	var ans = `${canadianGeoNamesService}?bbox=${area_x0},${area_y0},${area_x1},${area_y1}&num=${maxItems}`;
	return ( ans );
}

async function getCsv(url){
	var response = await fetch(url); 
	var txt = await response.text();
	txt = txt.split("\n");
	var ans = [];
	for ( var line of txt ){
		// https://www.ipentec.com/document/csharp-read-csv-file-by-regex ダブルクォーテーションエスケープを加味したcsvパース
		line = line.split(/,(?=(?:[^"]*"[^"]*")*[^"]*$)/);
		if (line.length > 1){
			ans.push(line);
		}
	}
	return ( ans );
}

function drawPoints(csv){
	removeUses();
	var schema = csv[0].join();
	var latCol=csv[0].indexOf("latitude");
	var lngCol=csv[0].indexOf("longitude");
	svgImage.documentElement.setAttribute("property",schema);
	for ( var i = 1 ; i < csv.length ; i++){
		var point = csv[i];
		var meta = point.join();
		var lat = Number(point[latCol]);
		var lng = Number(point[lngCol]);
		var use=svgImage.createElement("use");
		use.setAttribute("xlink:href","#p0");
		use.setAttribute("content",meta);
		use.setAttribute("x",0);
		use.setAttribute("y",0);
		use.setAttribute("transform",`ref(svg,${lng},${-lat})`);
		svgImage.documentElement.appendChild(use);
	}
	svgMap.refreshScreen();
}

function removeUses(){
	var uses = svgImage.getElementsByTagName("use");
	for ( var i = uses.length-1 ; i >=0 ; i--){
		uses[i].remove();
	}
}
個人用ツール
名前空間

変種
操作
案内
ツール
Translate