2018年11月30日金曜日

ol5.3ex 1 - Simple Map

「Simple Map (simple.html) 」を参考に地図を表示してみます。説明に次のようにあります。

A simple map with an OSM source.

OSM ソースを使用したシンプル(単純な)マップ。

次の内容で index.js と index.html を作成します。(テストで使ったファイルの内容と同じです。)
user@deb9-vmw:~/new-project$ vim index.js
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import OSM from 'ol/source/OSM';
const map = new Map({
/** const[JavaScript]
 * Constants are block-scoped, much like variables defined 
 * using the let statement. The value of a constant cannot 
 * change through reassignment, and it can't be redeclared.
 * 定数(const)はブロックスコープで、let ステートメントを
 * 使用して定義された変数にとても似ています。定数の値は、再
 * 代入による変更はできず、再宣言されません。
 * (MDN[https://developer.mozilla.org/en-US/docs/Web/
 * JavaScript/Reference/Statements/const])
 */
/** Map[ol/Map]
 * The map is the core component of OpenLayers. For a map 
 * to render, a view, one or more layers, and a target 
 * container are needed:
 * map(マップ)は、OpenLayers のコアコンポーネントです。
 * map を描画するために、view(ビュー)と1つ以上の layers
 * (レイヤ)と target(ターゲット)コンテナが必要です:
 * (ol5 API)
 */
 target: 'map',
 /** target[new Map(options)]
  * The container for the map, either the element itself 
  * or the id of the element. If not specified at 
  * construction time, module:ol/Map~Map#setTarget must 
  * be called for the map to be rendered.
  * map のコンテナ、(HTML)エレメント自身、またはエレメント
  * の id。構築時に指定されていない場合、描画される map のた
  * めにモジュール:ol/Map~Map#setTarget が呼び出されなけれ
  * ばなりません。
  * (ol5 API)
  */
 layers: [
 /** layers[new Map(options)]
  * Layers. If this is not defined, a map with no layers 
  * will be rendered. Note that layers are rendered in 
  * the order supplied, so if you want, for example, a 
  * vector layer to appear on top of a tile layer, it 
  * must come after the tile layer.
  * layers(レイヤ)。これが定義されていない場合、layers のな
  * い map が描画されます。layers は提供される順番で描画され、
  * もし、例えば、タイルレイヤの上部にベクタレイヤを表示したい
  * なら、タイルレイヤの後に来なければなりません。
  * (ol5 API)
  */
  new TileLayer({
   source: new OSM()
  })
 ],
 view: new View({
 /** view[new Map(options)]
  * The map's view. No layer sources will be fetched unless
  * this is specified at construction time or through 
  * module:ol/Map~Map#setView.
  * map(マップ)の view(ビュー)。これが構築時に指定されてい
  * ないか、モジュール:ol/Map~Map#setView を通じてなければ、
  * 取るレイヤソースはありません。
  * (ol5 API)
  */
 /** View(ol/View)
  * A View object represents a simple 2D view of the map.
  * This is the object to act upon to change the center, 
  * resolution, and rotation of the map.
  * View(ビュー)オブジェクトは、map(マップ)の 単純な 2D 
  * view を表示します。
  * これは、map の center(中心)、resolution(解像度)、
  * rotation (回転)を変換を実行するためのオブジェクトです。
  * (ol5 API)
  */
  center: [0, 0],
  /** center[new View(opt_options)]
   * The initial center for the view. The coordinate system 
   * for the center is specified with the projection option. 
   * Layer sources will not be fetched if this is not set, 
   * but the center can be set later with #setCenter.
   * view(ビュー)の初期 center(中心)値。 center の座標系
   * は、projection(投影法)オプションで指定されます。これが
   * 設定されなければレイヤソースは取得されませんが、center は 
   * #setCenter で後で設定できます。
   * (ol5 API)
   */
  zoom: 2
  /** zoom[new View(opt_options)]
   * Only used if resolution is not defined. Zoom level used 
   * to calculate the initial resolution for the view. The 
   * initial resolution is determined using the 
   * #constrainResolution method.
   * resolution(解像度)が定義されていない場合のみ使用されま
   * す。view の初期 resolution(解像度)を計算するために使用
   * される zoom(ズーム)レベル。初期 resolution は、
   * #constrainResolution メソッドを使用して決定されます。
   * (ol5 API)
   */
 })
});

user@deb9-vmw:~/new-project$ vim index.html
<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
  <title>Simple Map</title>
  <!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
  <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script>
 </head>
 <body>
  <div id="map"></div>
  <script src="./index.js"></script>
 <body>
</html>
npm start を実行します。
user@deb9-vmw:~/new-project$ npm start
> new-project@1.0.0 start /home/user/ol5-examples01

> parcel index.html

Server running at http://localhost:1234

✨  Built in 16.23s.

Webブラウザのアドレス欄に

http://localhost:1234

と入力して Enter キーを押します。


ol5.3ex 0 - Examples のテストの準備
examples 用のディレクトリを作成し、そこに移動します。

user@deb9-vmw:~$ mkdir ol5-examples01 &&  cd ol5-examples01

このプロジェクトを初期化し、問い合わせに回答します。

user@deb9-vmw:~/ol5-examples01$  npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install ` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (ol5-examples01)
version: (1.0.0)
description: ol5 examples test
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /home/user/ol5-examples01/package.json:

{
  "name": "ol5-examples01",
  "version": "1.0.0",
  "description": "ol5 examples test",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this OK? (yes) yes

OpenLayers をインストールします。

user@deb9-vmw:~/ol5-examples01$ npm install ol
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN ol5-examples01@1.0.0 No repository field.

+ ol@5.3.0
added 8 packages from 6 contributors and audited 8 packages in 4.063s
found 0 vulnerabilities

開発環境としてビルドツールの parcel をインストールします。

user@deb9-vmw:~/ol5-examples01$ npm install --save-dev parcel-bundler
npm WARN deprecated browserslist@1.7.7: Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools.

> deasync@0.1.13 install /home/user/ol5-examples01/node_modules/deasync
> node ./build.js

`linux-x64-node-8` exists; testing
Binary is fine; exiting

> parcel-bundler@1.10.3 postinstall /home/user/ol5-examples01/node_modules/parcel-bundler
> node -e "console.log('\u001b[35m\u001b[1mLove Parcel? You can now donate to our open collective:\u001b[22m\u001b[39m\n > \u001b[34mhttps://opencollective.com/parcel/donate\u001b[0m')"

Love Parcel? You can now donate to our open collective:
 > https://opencollective.com/parcel/donate
npm WARN ol5-examples01@1.0.0 No repository field.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})

+ parcel-bundler@1.10.3
added 735 packages from 575 contributors and audited 7635 packages in 42.851s
found 0 vulnerabilities

テストのために、次の内容で index.js と index.html を作成します。

user@deb9-vmw:~/new-project$ vim index.js
import 'ol/ol.css';
import {Map, View} from 'ol';
import TileLayer from 'ol/layer/Tile';
import OSM from 'ol/source/OSM';

const map = new Map({
 target: 'map',
 layers: [
  new TileLayer({
   source: new OSM()
  })
 ],
 view: new View({
  center: [0, 0],
  zoom: 0
 })
});

user@deb9-vmw:~/new-project$ vim index.html
<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
  <title>Using Parcel with OpenLayers</title>
  <style>
   #map {
    width: 400px;
    height: 250px;
   }
  </style>
 </head>
 <body>
  <div id="map"></div>
  <script src="./index.js"></script>
 <body>
</html>
package.json の script セクションに次のように追加します。

user@deb9-vmw:~/public_html/new-project$ vim package.json
{
  "name": "browserify_demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "parcel index.html",
    "build": "parcel build --public-url . index.html"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "ol": "^5.1.3"
  },
    "parcel-bundler": "^1.9.7"
  }
}
npm start を実行します。

user@deb9-vmw:~/new-project$ npm start
> new-project@1.0.0 start /home/user/ol5-examples01

> parcel index.html


Server running at http://localhost:1234

✨  Built in 16.23s.

Webブラウザのアドレス欄に

http://localhost/:1234

と入力して Enter キーを押します。


v5.3.0 がリリースされました

v5.3.0 がリリースされました
日本時間で(2018.11.7)に v5.3.0 がリリースされました。

Releases 5.3.0 - openlayers/openlayers GitHub
(https://github.com/openlayers/openlayers/releases/tag/v5.3.0)より

We're continuing to work toward more complete type checking with TypeScript – based on JSDoc annotations in the source. The 5.3 release includes a number of bug fixes related to the type checking effort. In addition the release comes with a handful of new features and improved API reference docs.

ソース内の JSDoc annotations を基本とした - TypeScript を使用してより完全なタイプチェックに向かって作業しています。5.3 リリースは、2、3のタイプチェック効果に関係したバグフィックスを含みます。加えて、リリースは少しの新しい機能と向上した API リファレンスドキュメントを備えています。


Upgrade Notes

The getUid function returns string
getUid ファンクションは文字列を返します

The getUid function from the ol/util module now returns a string instead of a number.

ol/util モジュールからの getUid ファンクションは、現在、数字のかわりに文字列を返します。


Attributions are not collapsible for ol/source/OSM
Attributions は ol/source/OSM に対して折りたたみできません

When a map contains a layer from a ol/source/OSM source, the ol/control/Attribution control will be shown with the collapsible: false behavior.

map は ol/source/OSM ソースのレイヤを含むとき、ol/control/Attribution コントロールは collapsible: false 動作で表示されます。

To get the previous behavior, configure the ol/control/Attribution control with collapsible: true.

以前の動作を求めるためには、ol/control/Attribution コントロールをcollapsible: true に設定します。

(New Features and Fixes は、リストを参照して下さい。)

2018年10月31日水曜日

OpenLayers5 Workshop - 5 Deploying

5 Deploying
5 デプロイ
(訳注:デプロイとは、主にネットワークを通じて提供されるWebアプリケーションなどのシステム開発工程において、システムを利用可能な状態にすることである。[Webilo 辞書、IT用語辞典「デプロイ:https://www.weblio.jp/content/%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4」より]

Throughout the workshop, we've been using a development server to view the examples. This is similar to the setup you would use when developing an application with the ol package. When you're ready to deploy your application, you'll want to create a minified bundle of your application entry point with a build step.

ワークショップを通じて、例を眺めるために開発サーバを使っています。これは、ol パッケージでアプリケーションを開発するとき使うセットアップに似ています。アプリケーションをデプロイする準備ができているとき、ビルド段階でアプリケーションのエントリポイントの縮小版ビルドを作成したくなります。
(訳注:エントリーポイントとは、プログラムを実行するうえで、プログラムやサブルーチンの実行を開始する場所のこと。[ウィキペディア「エントリポイント:https://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%B3%E3%83%88%E3%83%AA%E3%83%BC%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88」より]

We've been using webpack for module bundling during development. The webpack.config.js at the root of the workshop directory includes our webpack configuration profile. When we started the development server with npm start, we were running webpack in development mode. In production mode, the bundle is minified. This provides a good starting point for your webpack configuration. Explore the other webpack plugins to see what else you might want to bring in.

開発する間、モジュールバンドルのウェブパックを使います。ワークショップディレクトリのルートにある webpack.config.js は、ウェブパックコンフィギュレーションプロファイルを含みます。npm start で開発サーバを開始したとき、development(開発)モードでウェブパックを運用しています。production(本番)モードで、バンドルは縮小されます。これはウェブパックコンフィギュレーションの良い開始点を提供します。他に取り入れたいものを確かめるために、他のウェブパックプラグインを探してください。

(訳注:広義のモジュールバンドルとは、正しい順番で、複数のモジュール(及び依存性)を1つ(あるいは複数)のファイルへとくっつけることを言います。[Qiita「[意訳]初学者のためのJavaScriptモジュール講座 Part2、モジュールバンドルとは:https://qiita.com/chuck0523/items/c88abe4fac828b7b5f4e」より]

To build assets for deployment, we run our build script from package.json:

デプロイのためのアセットをビルドするために、package.json から build スクリプトを実行します:

(訳注:asset(アセット)は、「資産、財産」という意味ですが、ここでは、作成した html や js ファイルを含む、デプロイに必要なものだと思われます。)

npm run build

This runs webpack --mode=production, but doesn't require that webpack is on our path.

これは、webpack --mode=production を実行しますが、webpack がパス上にある必要はありません。

After the build finishes, you'll have artifacts in the build directory. These are the assets that you would deploy to your production server (or S3, or wherever you want to host your application). You can see what the app looks like by opening the index.html file in your browser.

ビルドが終了した後、build ディレクトリにアーティファクトを得ます。これらは、本番サーバ(または S3、または、アプリケーションを提供したいところどこでも)にデプロイするアセットです。ブラウザで index.html ファイルを開くことによってアプリのようにみえることが確認できます。

(訳注:artifact(アーティファクト)は、「人工物、工芸品」という意味ですが、ここでは、ビルドで生成したしたファイルやフォルダだと思われます。)

open build/index.html

That's it. You're done!

それでおしまい。終了です!

OpenLayers5 Workshop - 4.3 Render sea level

4 Raster Operations
4.3 Render sea level
平均海面を描画

In the previous step, we rendered the Terrain-RGB tiles directly on the map. What we want to do is render sea level on the map instead. And we want users to be able to adjust the height above sea level and see the adjusted height rendered on the map. We'll use a raster source to work with the elevation data directly and get the user input from an input slider on the page.

前回のステップで、Terrain-RGB タイルを直接マップ上に描画しました。(今回)したいことは、かわりに、平均海面をマップ上に描画します。そして、ユーザが海抜の高さを調整し、マップ上に描画された調節された高さを見ることができるようにします。elevation(標高)データで直接操作するためにラスタソースを使い、ページ上のインプットスライダからユーザインプット(input)を取得します。

Let's add the controls to the page first. In your index.html, add the following label and input slider:

最初にコントロ0るをページに追加しましょう。index.html に次のラベルとインプットスライダを追加します:
<label id="slider">
 Sea level
 <input id="level" type="range" min="0" max="100" value="1"/>
 +<span id="output"></span> m
</label>
Now add some style to those controls (in the <style> of your index.html):

では、(index.html の <style> に)いくつかのスタイルをそれらのコントロールに追加します:
#slider {
 position: absolute;
 bottom: 1rem;
 width: 100%;
 text-align: center;
 text-shadow: 0px 0px 4px rgba(255, 255, 255, 1);
}
Instead of directly rendering the R, G, B, A values from the Terrain-RGB tiles, we want to manipulate the pixel values before rendering. The raster source allows you to do this by accepting any number of input sources and an operation. This operation is a function that gets called for every pixel in the input sources. We only have one input source (elevation), so it will get called with an array of one pixel, where a pixel is a [red, green, blue, alpha] array. The operation also gets called with a data object. We'll use the data object to pass along the value of the input slider.

Terrain-RGB タイルから R、G、B、A 値を直接描画するかわりに、描画する前にピクセル値を操作するようにします。ラスタソース(raster source)は、任意の数のインプットソース(input source)と operation(オペレーション)を受け取ることによってこれを行うことを許可します。この operation はインプットソースのすべてのピクセルの呼び出し(call)を取得するファンクションです。1つのインプットソース(elevation)だけ持ち、そのため、1つのピクセルの配列を伴う呼び出しを取得し、ピクセルは、[red, green, blue, alpha] 配列になっています。operation は data(データ)オブジェクトを伴う呼び出しも取得します。インプットスライダの値に沿って渡すために data オブジェクトを使います。

First, import the RasterSource and ImageLayer (in main.js):

最初に、(main.js に)RasterSource と ImageLayer をインポートします:
import ImageLayer from 'ol/layer/Image';
import RasterSource from 'ol/source/Raster';
Add the function below to your main.js. This function decodes the input elevation data — transforming red, green, and blue values into a single elevation measure. For elevation values at or below the user selected value, the function returns a partially transparent blue pixel. For values above the user selected value, the function returns a transparent pixel.

下の function を main.js に追加します。このファンクションは、インプット elevation データを、赤(red)、緑(green)、青(blue)の値を単一の elevation 量に変換するために、デコード(復号化)します。ユーザが選択した、または、それより低い elevation 値の場合には、ファンクションは部分的に透明な青ピクセルを返します。ユーザが選択した値より高い場合には、ファンクションは透明なピクセルを返します。
function flood(pixels, data) {
 const pixel = pixels[0];
 if (pixel[3]) {
  // decode R, G, B values as elevation
  const height = -10000 + ((pixel[0] * 256 * 256 + pixel[1] * 256 + pixel[2]) * 0.1);
  if (height <= data.level) {
   // sea blue
   pixel[0] = 145; // red
   pixel[1] = 175; // green
   pixel[2] = 186; // blue
   pixel[3] = 255; // alpha
  } else {
   // transparent
   pixel[3] = 0;
  }
 }
 return pixel;
}
Create a raster source with a single input source (the elevation data), and configure it with the flood operation.

単一の入力ソース(elevation データ)でラスターソースを作成し、 flood operation でそれを設定します。
const raster = new RasterSource({
 sources: [elevation],
 operation: flood
});
Listen for changes on the slider input and re-run the raster operations when the user adjusts the value.

ユーザが値を調節するとき、スライダインプット(input)と raster operation の再実行の変更に対してリッスンします。
const control = document.getElementById('level');
const output = document.getElementById('output');
control.addEventListener('input', function() {
 output.innerText = control.value;
 raster.changed();
});
output.innerText = control.value;
The beforeoperations event is fired before the pixel operations are run on the raster source. This is our opportunity to provide additional data to the operations. In this case, we want to make the range input value (meters above sea level) available.

beforeoperations イベントは、pixel operations がラスタソースに対して実行される前に、始動します。これは、追加 data を operations に提供するための契機です。この場合、入力された値の範囲(標高メートル)を利用可能にします。
raster.on('beforeoperations', function(event) {
 event.data.level = control.value;
});
Finally, render the output from the raster operation by adding the source to an image layer. Replace the tile layer with an image layer that uses our raster source (modify the layers array in main.js):

最後に、ソースをイメージレイヤに追加することによって raster operation から出力を描画します。ラスタソースを使用するイメージレイヤで タイルレイヤを置き換えます(main.js で layers 配列を修正します):
new ImageLayer({
 opacity: 0.8,
 source: raster
})
With all this in place, the map should now have a slider that let's users control changes in sea level.

すべてこれを正しい場所に用いると、マップはユーザが海面の変化を調節できるスライダを保持できます。

Sea level rise in Boston

■□ Debian9 で試します■□
「Render sea level」の例を表示します。「Map setup」で使用した index.html のバックアップを保存して次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ cp index.html index.html_mapsetup
user@deb9-vmw:~/openlayers-workshop-en$ vim index.html
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>OpenLayers</title>
  <style>
   html, body, #map-container {
    margin: 0;
    height: 100%;
    width: 100%;
    font-family: sans-serif;
    }
   #slider {
    position: absolute;
    bottom: 1rem;
    width: 100%;
    text-align: center;
    text-shadow: 0px 0px 4px rgba(255, 255, 255, 1);
   }
  </style>
 </head>
 <body>
  <div id="map-container"></div>
  <label id="slider">
   Sea level
   <input id="level" type="range" min="0" max="100" value="1"/>
   +<span id="output"></span> m
  </label>
 </body>
</html>
「Render elevation data」で使用した main.js のバックアップを保存して次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ cp main.js main.js_elevation
user@deb9-vmw:~/openlayers-workshop-en$ vim main.js
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import XYZSource from 'ol/source/XYZ';
import {fromLonLat} from 'ol/proj';
import ImageLayer from 'ol/layer/Image';
import RasterSource from 'ol/source/Raster';
const key = '<your-default-public-token>';
const elevation = new XYZSource({
 url: 'https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.pngraw?access_token=' + key,
 crossOrigin: 'anonymous'
});
function flood(pixels, data) {
 const pixel = pixels[0];
 if (pixel[3]) {
  // decode R, G, B values as elevation
  const height = -10000 + ((pixel[0] * 256 * 256 + pixel[1] * 256 + pixel[2]) * 0.1);
  if (height <= data.level) {
   // sea blue
   pixel[0] = 145; // red
   pixel[1] = 175; // green
   pixel[2] = 186; // blue
   pixel[3] = 255; // alpha
  } else {
   // transparent
   pixel[3] = 0;
  }
 }
 return pixel;
}
const raster = new RasterSource({
 sources: [elevation],
 operation: flood
});
const control = document.getElementById('level');
const output = document.getElementById('output');
control.addEventListener('input', function() {
 output.innerText = control.value;
 raster.changed();
});
output.innerText = control.value;
raster.on('beforeoperations', function(event) {
 event.data.level = control.value;
});
new Map({
 target: 'map-container',
 layers: [
  new TileLayer({
   source: new XYZSource({
    url: 'http://tile.stamen.com/terrain/{z}/{x}/{y}.jpg'
   })
  }),
  new ImageLayer({
   opacity: 0.8,
   source: raster
  })
 ],
 view: new View({
  center: fromLonLat([-71.06, 42.37]),
  zoom: 12
 })
});
http://localhost:3000/ とブラウザでマップを開きます。(もし開かなければ、'npm start' を実行してください。



OpenLayers5 Workshop - 4.2 Render elevation data

4 Raster Operations
4.2 Render elevation data
標高データを描画

We're going to work with elevation data that is encoded in PNG tiles (see the Mapbox post on Terrain-RGB for more detail). For this exercise, you'll need to sign up for a Mapbox account and use your access token for tiles.

PNG(ピングフォーマット画像)タイルでエンコードされる elevation(標高)データを使って作業します(さらに詳細は Mapbox に搭載の Terrain-RGB を参照)。この演習のために、Mapbox アカウントのサインアップとタイルのためのアクセストークンを使うことが必要です。

Add your default public token to main.js:

デフォルトのパブリックトークンを main.js を追加します:

const key = '<your-default-public-token>';

We want to manipulate the elevation data before rendering, but initially we'll add the Terrain-RGB tiles to the map just to see what they look like. To do this, create an XYZ source with the Terrain-RGB URL and your access token.

描画の前に elevation データを操作したいのですが、最初に、どのように見えるか単に見るため、Terrain-RGB タイルをマップに追加します。これを実行するため、Terrain-RGB URL とアクセストークンで XYZ ソースを作成します。
const elevation = new XYZSource({
 url: 'https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.pngraw?access_token=' + key,
 crossOrigin: 'anonymous'
});
Next, create a tile layer that uses the elevation source. Add this layer your map's layers array in main.js:

次に、elevation ソースを使うタイルレイヤを作成します。 main.js でこのレイヤをマップレイヤ配列に追加します:
new TileLayer({
 opacity: 0.8,
 source: elevation
})
You should now see some oddly colored tiles shown over your base layer. The elevation data in the Terrain-RGB tiles is encoded in the red, green, and blue channels. So while this data isn't meant to be rendered directly, it is interesting to look at.

ベースレイヤを覆って表示される奇妙なカラータイルが見られます。Terrain-RGB タイルの標高データは、赤、緑、青チャンネルにエンコードされます。このデータは、直接描画されるものではありませんが、見るのは興味深いです。

Terrain-RGB tiles rendered over Boston

■□ Debian9 で試します■□
「Render elevation data」の例を表示します。「Map setup」で使用した main.js のバックアップを保存して次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ cp main.js main.js_mapsetup
user@deb9-vmw:~/openlayers-workshop-en$ vim main.js
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import XYZSource from 'ol/source/XYZ';
import {fromLonLat} from 'ol/proj';
const key = '<your-default-public-token>';
const elevation = new XYZSource({
 url: 'https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.pngraw?access_token=' + key,
 crossOrigin: 'anonymous'
});
new Map({
 target: 'map-container',
 layers: [
  new TileLayer({
   source: new XYZSource({
    url: 'http://tile.stamen.com/terrain/{z}/{x}/{y}.jpg'
   })
  }),
  new TileLayer({
   opacity: 0.8,
   source: elevation
  })
 ],
 view: new View({
  center: fromLonLat([-71.06, 42.37]),
  zoom: 12
 })
});
http://localhost:3000/ とブラウザでマップを開きます。(もし開かなければ、'npm start' を実行してください。



OpenLayers5 Workshop - 4.1 Map setup

4 Raster Operations
ラスタ操作

Up to this point, when we have used raster data (with an XYZ tile source for example), we have used it for presentation purposes only — rendering the data directly to the map. It is also possible to work with the pixel values in the data we fetch, run operations on these values, and manipulate things before rendering. The Raster source provides a way to run pixel-wise operations on data from any number of input sources. When the source is used in an Image layer, the result of the raster operation can be rendered on the map.

ここまでで、(example の XYZ タイルソースを使用した)ラスタデータ使うとき、プレゼンテーション目的だけで使い、データを直接マップに描画します。描画前にものを取得して、これらの値に関する操作を実行し、操作するデータにピクセル値で動作することも可能です。ラスタソースは、任意の数の入力ソースからデータに関するピクセル関連の操作を実行する方法を提供します。ソースはイメージ(Image)レイヤで使用されるとき、ラスタ操作の結果は、マップ上に描画されます。

In these exercises, we'll work with elevation data served as XYZ tiles. Instead of rendering the encoded elevation data directly, we'll run a pixel-wise operation on the data before rendering.

この演習で、 XYZ アイルとして供給される標高データを使って作業します。エンコードされた標高データを直接描画するかわりに、描画の前にデータに関するピクセル関連の操作を実行します。

● Map setup
● Render elevation data
● Render sea level

● マップ装備
● 標高データを描画
● 海抜を描画

4.1 Map setup
マップ装備

Edit your index.html so we're ready to render a full page map:

全画面マップを描画する準備のために index.html を編集します:
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>OpenLayers</title>
  <style>
   html, body, #map-container {
    margin: 0;
    height: 100%;
    width: 100%;
    font-family: sans-serif;
    }
  </style>
 </head>
 <body>
  <div id="map-container"></div>
 </body>
</html>
We'll start out with a map centered on Boston showing a single XYZ source. Update your main.js so it looks like this:

単一の XYZ ソースを表示するボストンを中心としたマップで始めます。このように見えるように main.js を更新します:
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import XYZSource from 'ol/source/XYZ';
import {fromLonLat} from 'ol/proj';
new Map({
 target: 'map-container',
 layers: [
  new TileLayer({
   source: new XYZSource({
    url: 'http://tile.stamen.com/terrain/{z}/{x}/{y}.jpg'
   })
  })
 ],
 view: new View({
  center: fromLonLat([-71.06, 42.37]),
  zoom: 12
 })
});
A map of Boston

■□ Debian9 で試します■□
「Map setup」の例を表示します。「Making things look bright」で使用した index.html のバックアップを保存して次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ cp index.html index.html_bright
user@deb9-vmw:~/openlayers-workshop-en$ vim index.html
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>OpenLayers</title>
  <style>
   html, body, #map-container {
    margin: 0;
    height: 100%;
    width: 100%;
    font-family: sans-serif;
    }
  </style>
 </head>
 <body>
  <div id="map-container"></div>
 </body>
</html>
「Making things look bright」で使用した main.js のバックアップを保存して次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ cp main.js main.js_bright
user@deb9-vmw:~/openlayers-workshop-en$ vim main.js
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import XYZSource from 'ol/source/XYZ';
import {fromLonLat} from 'ol/proj';
new Map({
 target: 'map-container',
 layers: [
  new TileLayer({
   source: new XYZSource({
    url: 'http://tile.stamen.com/terrain/{z}/{x}/{y}.jpg'
   })
  })
 ],
 view: new View({
  center: fromLonLat([-71.06, 42.37]),
  zoom: 12
 })
});
http://localhost:3000/ とブラウザでマップを開きます。(もし開かなければ、'npm start' を実行してください。



OpenLayers5 Workshop - 3.4 Making things look bright

3 Vector Tile
3.4 Making things look bright
ものを明るく見せる

There have been many attempts to create tools and formats for styling maps. The most popular formats were probably SLD and CartoCSS. A graphical tool that comes to mind is Atlas Styler. But none of these format or tools were really convenient to use.

マップをスタイルするためにツールとフォーマットを作成する試みがたくさんあります。最も一般的なフォーマットは、おそらく、SLD と CartoCSS です。頭に浮かぶグラフィカルツールは、Atlas Styler です。これらのフォーマット、または、ツールは、使うのに本当に便利ではありません。

Mapbox finally came up with Mapbox Studio, a very user friendly style editor, and the Mapbox Style format. The Mapbox Style format is supported by a growing number of applications, and a graphical Open Source editor, Maputnik, is available for creating and modifying Mapbox Style files.

Mapbox は、とても使いやすいスタイルエディタ、Mapbox Studio と Mapbox Style フォーマットをついに出しました。Mapbox Style フォーマットは、増え続けるアプリケーションによって支援され、グラフィカル Open Source エディタ Maputnik は、Mapbox Style ファイルを作成し編集することに利用できます。

The ol-mapbox-style package adds support for the Mapbox Style format to OpenLayers. Let's see if we can get a nicer looking map than the one from the previous exercise.

ol-mapbox-style パッケージは、OpenLayers に Mapbox Style フォーマットのためのサポートを追加します。前回の演習からより見栄えの良いマップを取得できるかみてみます。

Creating Mapbox Style files in Maputnik
Maputnik で Mapbox Style ファイルを作成

You can play with a live instance of Maputnik at https://maputnik.github.io/editor/, or download and install it locally. I have created a file with our ugly style. You can download that file, ugly.json, and open it in Maputnik to verify it looks just as ugly there as it does in our OpenLayers application:

https://maputnik.github.io/editor/ にあるか、または、ローカルでダウンロードしインストールして Maputnik のインスタンス化で楽しめます。私は ugly スタイルでファイルを作成しています。あなたはそのファイル、ugly.json をダウンロードし、OpenLayers アプリケーションで実行するのと同じようにそこで ugly かを確かめるために Maputnik で開きます。

Ugly style in Maputnik

Create a map from a Mapbox Style in OpenLayers
OpenLayers で Mapbox Style からマップを作成

We'll import the apply function from the ol-mapbox-style package, so we can simply have a map with all the contents from a style file rendered into our map-container. But first we need to install the package:

ol-mapbox-style から apply ファンクションをインポートすると、map-container に描画されたスタイルファイルからすべてのコンテンツと一緒にマップを簡単に持つことができます。しかし、最初にパッケージをインストールする必要があります:

npm install ol-mapbox-style@beta

(訳注:実施には npm install ol-mapbox-style)

Now we can import the function into our main.js:

main.js にファンクションをインポートします:

import {apply} from 'ol-mapbox-style';

We can also remove all other imports except for these two:

これら2つを除いて他のインポートすべても削除します:
import 'ol/ol.css';
import Overlay from 'ol/Overlay';
While we are at cleaning up, we can also delete the key, map, layer definitions. And we can – of course – remove all the style code. Finally, in index.html, we can remove the line that adds the Open Sans font, and the background-color for the map.

クリーンアップの間、key、map、layer 定義も削除できます。そして、もちろん、スタイルコードをすべて削除できます。最後に、index.html で Open Sans ファンとを追加するラインとマップの background-color を削除します。

Instead of the previous map code, we need a single line to create a map from our ugly Mapbox style, which we insert below our imports:

前述のマップコードのかわりに、ugly Mapbox スタイルからマップを作成するための1つのラインが必要で、import の下に挿入します:

const map = apply('map-container', './data/ugly.json');

This should give us the same ugly map as before, just with less application code. But hey, we wanted to make things look bright, and not look at an ugly map again. So let's open the 'OSM Bright' from Maputnik's Gallery Styles, and use that. I have prepared it already, and made the result available as http://localhost:3000/data/bright.json. So let's change the apply() line to

これは前回と同じ ugly map を、より少ないアプリケーションコードで、与えます。しかし、おい、明るく見せたいし、また、 ugly(見苦しい)マップを見たくありません。それでは、Maputnik's Gallery Styles から 'OSM Bright' を開き、それを使います。それはすでに準備されていて、http://localhost:3000/data/bright.json として結果を利用できます。それでは、 apply() ラインを変更します:

const map = apply('map-container', './data/bright.json');

What a relief. Finally we can enjoy a nice looking world map, and zoom to Boston!

ホッとしました。最後に、見栄えの良い世界地図を楽しみ、ボストンにズームインします。

A bright map of Boston

And note that our map is still interactive, meaning you get an info popup when you click on any geometry.

このマップは、まだ、インタラクティブな状態で、ジオメトリをクリックしたとき情報ポッパップを取得することに注意してください。


■□ Debian9 で試します■□
「Interact with VectorTile features」の例を表示します。 最初に、ol-mapbox-style パッケージをインストールします。

nob61@deb9-vmw:~/openlayers-workshop-en$ npm install ol-mapbox-style@beta
npm WARN extract-text-webpack-plugin@3.0.2 requires a peer of webpack@^3.1.0 but none is installed. You must install peer dependencies yourself.

+ ol-mapbox-style@3.0.0-beta.9
added 2 packages from 1 contributor, removed 16 packages, updated 3 packages and audited 7504 packages in 10.782s found 0 vulnerabilities

「Styling a VectorTile layer」で使用した index.html のバックアップを保存して次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ cp index.html index.html_styling
user@deb9-vmw:~/openlayers-workshop-en$ vim index.html
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>OpenLayers</title>
  <style>
   html, body, #map-container {
    margin: 0;
    height: 100%;
    width: 100%;
    font-family: sans-serif;
    background-color: rgb(248, 244, 240);
   }
   .arrow_box {
    position: relative;
    background: #000;
    border: 1px solid #003c88;
   }
   .arrow_box:after, .arrow_box:before {
    top: 100%;
    left: 50%;
    border: solid transparent;
    content: " ";
    height: 0;
    width: 0;
    position: absolute;
    pointer-events: none;
   }
   .arrow_box:after {
    border-color: rgba(0, 0, 0, 0);
    border-top-color: #000;
    border-width: 10px;
    margin-left: -10px;
   }
   .arrow_box:before {
    border-color: rgba(0, 60, 136, 0);
    border-top-color: #003c88;
    border-width: 11px; 
    margin-left: -11px; 
   } 
   .arrow_box { 
    border-radius: 5px; 
    padding: 10px; 
    opacity: 0.8; 
    background-color: black; 
    color: white; 
   }
   #popup-content { 
    max-height: 200px; 
    overflow: auto; 
   } 
   #popup-content th { 
    text-align: left; 
    width: 125px; 
   }
  </style>
 </head>
 <body>
  <div id="map-container"></div>
  <div class="arrow_box" id="popup-container"> 
   <div id="popup-content"></div>
  </div>
 </body>
</html>
「Styling a VectorTile layer」」で使用した main.js のバックアップを保存して次のように修正します。削除するインポート(import)、key、layer、map の定義などは、ugly.json ファイルに記述されています。

user@deb9-vmw:~/openlayers-workshop-en$ cp main.js main.js_styling
user@deb9-vmw:~/openlayers-workshop-en$ vim main.js
import 'ol/ol.css';
import Overlay from 'ol/Overlay';
import {apply} from 'ol-mapbox-style'; 
const map = apply('map-container', './data/ugly.json'); 
const overlay = new Overlay({
 element: document.getElementById('popup-container'),
 positioning: 'bottom-center',
 offset: [0, -10],
 autoPan: true
});
map.addOverlay(overlay);
overlay.getElement().addEventListener('click', function() {
 overlay.setPosition();
});
map.on('click', function(e) {
 let markup = '';
 map.forEachFeatureAtPixel(e.pixel, function(feature) {
  markup += `${markup && '<hr>'}<table>`;
  const properties = feature.getProperties();
  for (const property in properties) {
   markup += `<tr><th>${property}</th><td>${properties[property]}</td></tr>`;
  }
  markup += '</table>';
 }, {hitTolerance: 1});
 if (markup) {
  document.getElementById('popup-content').innerHTML = markup;
  overlay.setPosition(e.coordinate);
 }
});
'npm start' を実行すると次のようなメッセージが表示され、コンパイルできませんました。

nob61@deb9-vmw:~/openlayers-workshop-en$ npm start
$gt; ol-workshop@0.0.0 start /home/nob61/openlayers-workshop-en
$gt; webpack-dev-server --mode=development

ℹ 「wds」: Project is running at http://localhost:3000/
ℹ 「wds」: webpack output is served from /
✖ 「wdm」: 
ERROR in ./node_modules/@mapbox/mapbox-gl-style-spec/util/color.js
Module parse failed: Unexpected token (17:5)
You may need an appropriate loader to handle this file type.
|  */
| class Color {
|     r: number;
|     g: number;
|     b: number;
 @ ./node_modules/ol-mapbox-style/index.js 23:0-60 190:38-43
 @ ./main.js
 @ multi (webpack)-dev-server/client?http://localhost:3000 ./main.js
---
ℹ 「wdm」: Failed to compile.

OpenLayers5 Workshop - 3.3 Styling a VectorTile layer

3 Vector Tile
3.3 Styling a VectorTile layer
VectorTile レイヤをスタイルする

Styling a vector tile layer works exactly the same way as styling a vector layer. We will now try to style our world map so it actually looks like a world map is supposed to look.

ベクタタイルレイヤをスタイルすることは、ベクタレイヤをスタイルするのと全く同じ方法で動作します。実際に見るはずの世界地図のように見えるよう世界地図をスタイルしてみます。

Load fonts
フォントをロードする

Let's say we want to use a nicer font for labels in our map. I decided to use the Open Sans font family, which can easily be loaded with an additional stylesheet in the <head> of our index.html:

マップでラベルに見た目が良いフォントを使いたいとします。Open Sans フォントファミリを使うことを決めましたが、index.html の <head> に追加のスタイルシートを使って簡単にロードできます。

<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">

Set a map background
マップの背景を設定する

A common style element in vector tile maps is a background color, which the user sees in places that are not covered by any geometries. Like we already saw in the vector exercise, this can be done by simply setting a background color in a <style> for the #map-container:

ベクタタイルマップで一般的なスタイルエレメントは背景色(background color)ですが、どのジオメトリにも覆われていない場所に見られます。すでにベクタ演習で見られたように、これは #map-container の <style> に背景色を単純に設定することによって実行できます。

background-color: rgb(248, 244, 240);

Style the layer with a style function
style ファンクションを使ってレイヤをスタイルする

Now we are going to add some application code to main.js.

では、main.js にいくつかアプリケーションコードを追加します。

First, we'll need imports for the styles we are going to use:

最初に、使うスタイルのインポートが必要です:

import {Style, Fill, Stroke, Circle, Text} from 'ol/style';
// import {createDefaultStyle} from 'ol/style/Style';
The style function is a bit long:

style ファンクションは少し長いです:
layer.setStyle(function(feature, resolution) {
 const properties = feature.getProperties();
 // Water polygons
 if (properties.layer == 'water') {
  return new Style({
   fill: new Fill({
    color: 'rgba(0, 0, 255, 0.7)'
   })
  });
 }
 // Boundary lines
 if (properties.layer == 'boundary' && properties.admin_level == 2) {
  return new Style({
   stroke: new Stroke({
    color: 'gray'
   })
  });
 }
 // Continent labels
 if (properties.layer == 'place' && properties.class == 'continent') {
  return new Style({
   text: new Text({
    text: properties.name,
    font: 'bold 16px Open Sans',
    fill: new Fill({
     color: 'black'
    }),
    stroke: new Stroke({
     color: 'white'
    })
   })
  });
 }
 // Country labels
 if (properties.layer == 'place' && properties.class == 'country' &&
  resolution < map.getView().getResolutionForZoom(5)) {
   return new Style({
    text: new Text({
     text: properties.name,
     font: 'normal 13px Open Sans',
     fill: new Fill({
      color: 'black'
     }),
     stroke: new Stroke({
      color: 'white'
     })
    })
   });
 }
 // Capital icons and labels
 if (properties.layer == 'place' && properties.capital) {
  const point = new Style({
   image: new Circle({
    radius: 5,
    fill: new Fill({
     color: 'black'
    }),
    stroke: new Stroke({
     color: 'gray'
    })
   })
  });
  if (resolution < map.getView().getResolutionForZoom(6)) {
   point.setText(new Text({
    text: properties.name,
    font: 'italic 12px Open Sans',
    offsetY: -12,
     fill: new Fill({
      color: '#013'
     }),
     stroke: new Stroke({
      color: 'white'
     })
   }));
  }
  return point;
 }
 // return createDefaultStyle(feature, resolution);
});
I think you will agree that we have not reached our goal of creating a beautiful world map:

美しい世界地図を作成する最終目的に達していないと考えていると思います:

An ugly world map

There is much more effort involved in styling a world map appropriately, and writing a style function in JavaScript is probably not the right tool. In the next exercise, we will learn a different way of loading and styling vector tile layers.

適切に世界地図をスタイリングするものに含まれているもっとたくさんの効果があり、JavaScript に style ファンクションを書くことは、おそらく正しいツールではありません。この演習で、ベクタタイルレイヤをロードしスタイリングする難しい方法を学びます。

■□ Debian9 で試します■□
「Interact with VectorTile features」の例を表示します。「The VectorTile layer」で使用した index.html のバックアップを保存して次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ cp index.html index.html_interact
user@deb9-vmw:~/openlayers-workshop-en$ vim index.html
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>OpenLayers</title>
  <link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet"> 
  <style>
   html, body, #map-container {
    margin: 0;
    height: 100%;
    width: 100%;
    font-family: sans-serif;
    background-color: rgb(248, 244, 240);
   }
   .arrow_box {
    position: relative;
    background: #000;
    border: 1px solid #003c88;
   }
   .arrow_box:after, .arrow_box:before {
    top: 100%;
    left: 50%;
    border: solid transparent;
    content: " ";
    height: 0;
    width: 0;
    position: absolute;
    pointer-events: none;
   }
   .arrow_box:after {
    border-color: rgba(0, 0, 0, 0);
    border-top-color: #000;
    border-width: 10px;
    margin-left: -10px;
   }
   .arrow_box:before {
    border-color: rgba(0, 60, 136, 0);
    border-top-color: #003c88;
    border-width: 11px; 
    margin-left: -11px; 
   } 
   .arrow_box { 
    border-radius: 5px; 
    padding: 10px; 
    opacity: 0.8; 
    background-color: black; 
    color: white; 
   }
   #popup-content { 
    max-height: 200px; 
    overflow: auto; 
   } 
   #popup-content th { 
    text-align: left; 
    width: 125px; 
   }
  </style>
 </head>
 <body>
  <div id="map-container"></div>
  <div class="arrow_box" id="popup-container"> 
   <div id="popup-content"></div>
  </div>
 </body>
</html>
「The VectorTile layer」」で使用した main.js のバックアップを保存して次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ cp main.js main.js_interact
user@deb9-vmw:~/openlayers-workshop-en$ vim main.js
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import MVT from 'ol/format/MVT';
import VectorTileLayer from 'ol/layer/VectorTile';
import VectorTileSource from 'ol/source/VectorTile';
import Overlay from 'ol/Overlay';
// See https://openmaptiles.com/hosting/ for terms and access key
const key = '<your-access-key-here>';
const map = new Map({
 target: 'map-container',
 view: new  View({
  center: [0, 0],
  zoom: 2
 })
});
const layer = new VectorTileLayer({
 source: new VectorTileSource({
  attributions: [
   '<a href="http://www.openmaptiles.org/" target="_blank">&copy; OpenMapTiles</a>',
   '<a href="http://www.openstreetmap.org/about/" target="_blank">&copy; OpenStreetMap contributors</a>'
  ],
  format: new MVT(),
  url: `https://free-{1-3}.tilehosting.com/data/v3/{z}/{x}/{y}.pbf.pict?key=${key}`,
  maxZoom: 14
 })
});
map.addLayer(layer); 
const overlay = new Overlay({
 element: document.getElementById('popup-container'),
 positioning: 'bottom-center',
 offset: [0, -10],
 autoPan: true
});
map.addOverlay(overlay);
overlay.getElement().addEventListener('click', function() {
 overlay.setPosition();
});
map.on('click', function(e) {
 let markup = '';
 map.forEachFeatureAtPixel(e.pixel, function(feature) {
  markup += `${markup && '<hr>'}<table>`;
  const properties = feature.getProperties();
  for (const property in properties) {
   markup += `<tr><th>${property}</th><td>${properties[property]}</td></tr>`;
  }
  markup += '</table>';
 }, {hitTolerance: 1});
 if (markup) {
  document.getElementById('popup-content').innerHTML = markup;
  overlay.setPosition(e.coordinate);
 } else {
  overlay.setPosition();
 }
});
layer.setStyle(function(feature, resolution) {
 const properties = feature.getProperties();
 // Water polygons
 if (properties.layer == 'water') {
  return new Style({
   fill: new Fill({
    color: 'rgba(0, 0, 255, 0.7)'
   })
  });
 }
 // Boundary lines
 if (properties.layer == 'boundary' && properties.admin_level == 2) {
  return new Style({
   stroke: new Stroke({
    color: 'gray'
   })
  });
 }
 // Continent labels 
 if (properties.layer == 'place' && properties.class == 'continent') {
  return new Style({
   text: new Text({
    text: properties.name,
    font: 'bold 16px Open Sans',
    fill: new Fill({
     color: 'black'
    }),
    stroke: new Stroke({
     color: 'white'
    })
   })
  });
 }
 // Country labels 
 if (properties.layer == 'place' && properties.class == 'country' &&
     resolution < map.getView().getResolutionForZoom(5)) {
  return new Style({
   text: new Text({
    text: properties.name,
    font: 'normal 13px Open Sans',
    fill: new Fill({
     color: 'black'
    }),
    stroke: new Stroke({
     color: 'white'
    })
   })
  });
 }
 // Capital icons and labels 
 if (properties.layer == 'place' && properties.capital) {
  const point = new Style({
   image: new Circle({
    radius: 5,
    fill: new Fill({
     color: 'black'
    }),
    stroke: new Stroke({
     color: 'gray'
    })
   })
  });
  if (resolution < map.getView().getResolutionForZoom(6)) {
   point.setText(new Text({
    text: properties.name,
    font: 'italic 12px Open Sans',
    offsetY: -12,
    fill: new Fill({
     color: '#013'
    }),
    stroke: new Stroke({
     color: 'white'
    })
   }));
  }
  return point;
 }
 // return createDefaultStyle(feature, resolution);
});
http://localhost:3000/ とブラウザでマップを開きます。(もし開かなければ、'npm start' を実行してください。



OpenLayers5 Workshop - 3.2 Interact with VectorTile features

3 Vector Tile
3.2 Interact with VectorTile features
VectorTile フィーチャを使ってインタラクション

If we want to style the layer we just created, it would be nice to get some information about each geometry we see on the map. The nice thing about vector tile layers is that we can interact with them just like with vector layers. So it is easy to add a listener for clicks to the map, query the features at the clicked position, and display a popup with the attributes of each feature.

今、作成されたレイヤをスタイル(装飾)したいなら、マップ上に見える個々のジオメトリについて情報を取得することは良いことです。ベクタタイルレイヤについて良いことは、ベクタレイヤと同じくそれらを情報交換できるということです。マップにクリックリスナを追加することは簡単なので、クリックされた場所のフィーチャに問い合わせ、個々のフィーチャの属性と一緒にポップアップを表示します。

Adding a popup
ポップアップを追加

We create a popup style by going to http://www.cssarrowplease.com/. I decided to choose a darker popup. The css is added to the <style> section of our index.html:

http://www.cssarrowplease.com/ に行ってポップアップを作成します。ここでは、暗いポップアップを選ぶことにしました。css は、index.html の <style> セクションに追加します:
.arrow_box {
 position: relative;
 background: #000;
 border: 1px solid #003c88;
}
.arrow_box:after, .arrow_box:before {
 top: 100%;
 left: 50%;
 border: solid transparent;
 content: " ";
 height: 0;
 width: 0;
 position: absolute;
 pointer-events: none;
}
.arrow_box:after {
 border-color: rgba(0, 0, 0, 0);
 border-top-color: #000;
 border-width: 10px;
 margin-left: -10px;
}
.arrow_box:before {
 border-color: rgba(0, 60, 136, 0);
 border-top-color: #003c88;
 border-width: 11px;
 margin-left: -11px;
}
.arrow_box {
 border-radius: 5px;
 padding: 10px;
 opacity: 0.8;
 background-color: black;
 color: white;
}
We are going to render scrollable HTML tables into our popup, so our markup for the popup also needs a <div> for the popup content:

ポップアップにスクロールできる HTML テーブル(表)を描画するので、ポップアップのためのマークアップにもポップアップコンテンツに <div> が必要です:
<div class="arrow_box" id="popup-container">
 <div id="popup-content"></div>
</div>
To style the tables, we add some more style to the <style> section of index.html:

テーブルをスタイルするために、index.html の <style> セクションにいくつかさらにスタイルを追加します。
#popup-content {
  max-height: 200px;
  overflow: auto;
}
#popup-content th {
  text-align: left;
  width: 125px;
}
In the application's main.js import the Overlay class:

アプリケーションの main.js に Overlay クラスをインポートします:

import Overlay from 'ol/Overlay';

Again in the application's main.js, we can now append the code for the popup's Overlay:

もう一度、アプリケーションの main.js に、ポップアップの Overlay に対するコードを、直ちに、追加できます:

const overlay = new Overlay({
 element: document.getElementById('popup-container'),
 positioning: 'bottom-center',
 offset: [0, -10],
 autoPan: true
});
map.addOverlay(overlay);
To make it easy to close the popup so it does not cover other features we might want to click, we add a click listener to the overlay, so it closes when we click on it:

クリックしたい他のフィーチャを覆わないようにポップアップを閉じるのを簡単にするために、クリックリスナをオーバレイに追加し、それにより、クリックしたときそれが閉じます。
overlay.getElement().addEventListener('click', function() {
 overlay.setPosition();
});
Calling setPosition() on an overlay sets an undefined position, which causes the overlay to disappear.

オーバレイ上の setPosition() を呼び出すことは、定義されていない場所を設定し、オーバレイを消します。

Fill the popup with feature attributes
ポップアップをフィーチャ属性で満たす

Now it is time to connect the popup to a click listener on the map. We append more code at the bottom of main.js:

今、マップ上のポップアップをクリックリスナに接続するときです。main.js の下部にさらに多くのコードを追加します:
map.on('click', function(e) {
 let markup = '';
 map.forEachFeatureAtPixel(e.pixel, function(feature) {
  markup += `${markup && '<hr>'}<table>`;
  const properties = feature.getProperties();
  for (const property in properties) {
   markup += `<tr><th>${property}</th><td>${properties[property]}</td></tr>`;
  }
  markup += '</table>';
 }, {hitTolerance: 1});
 if (markup) {
  document.getElementById('popup-content').innerHTML = markup;
  overlay.setPosition(e.coordinate);
 } else {
  overlay.setPosition();
 }
});
By iterating through all the features we get at the clicked position (map.forEachFeatureAtPixel), we build a separate table for each feature. With each feature, we iterate through its properties (feature.getProperties()), and add a table row (<tr>) for each property. We also set a hitTolerance of 1 pixel to make it easier to click on lines.

すべてのフィーチャに渡って繰り返すことによって、クリックされた位置を取得し(map.forEachFeatureAtPixel)、個々のフィーチャに対する分かれたテーブルを構築します。個々のフィーチャで、そのプロパティに渡って繰り返し(feature.getProperties())、個々のプロパティに対してテーブルロー(列)を追加します。線上をクリックするのを簡単にするために1ピクセルの hitTolerance の設定もします。

Using the interactivity to build a style for our map
インタラクティビティをマップのスタイルを構築するために使う

Now we can click on any geometry in the map, and use the information we get in the popup to create styles in the next exercise. Note that vector tile features have a special layer property, which indicates the source layer (i.e. the layer the feature belongs to in the vector tile's structure, which is a layer -> feature hierarchy).

現在、マップのすべてのジオメトリ上をクリックでき、次の演習でスタイルを作成するためにポップアップで取得する情報を使います。ベクタタイルフィーチャは特別なレイヤプロパティを持ち、それは、ソースレイヤを示します(例えば、フィーチャが所属しているベクタタイル構造のレイヤで、それは レイヤ -> フィーチャヒエラルキです)。

Getting feature information

■□ Debian9 で試します■□
「Interact with VectorTile features」の例を表示します。「The VectorTile layer」で使用した index.html のバックアップを保存して次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ cp index.html index.html_vectortile
user@deb9-vmw:~/openlayers-workshop-en$ vim index.html
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>OpenLayers</title>
  <style>
   html, body, #map-container {
    margin: 0;
    height: 100%;
    width: 100%;
    font-family: sans-serif;
   }
   .arrow_box {
    position: relative;
    background: #000;
    border: 1px solid #003c88;
   }
   .arrow_box:after, .arrow_box:before {
    top: 100%;
    left: 50%;
    border: solid transparent;
    content: " ";
    height: 0;
    width: 0;
    position: absolute;
    pointer-events: none;
   }
   .arrow_box:after {
    border-color: rgba(0, 0, 0, 0);
    border-top-color: #000;
    border-width: 10px;
    margin-left: -10px;
   }
   .arrow_box:before {
    border-color: rgba(0, 60, 136, 0);
    border-top-color: #003c88;
    border-width: 11px; 
    margin-left: -11px; 
   } 
   .arrow_box { 
    border-radius: 5px; 
    padding: 10px; 
    opacity: 0.8; 
    background-color: black; 
    color: white; 
   }
   #popup-content { 
    max-height: 200px; 
    overflow: auto; 
   } 
   #popup-content th { 
    text-align: left; 
    width: 125px; 
   }
  </style>
 </head>
 <body>
  <div id="map-container"></div>
  <div class="arrow_box" id="popup-container"> 
   <div id="popup-content"></div>
  </div>
 </body>
</html>
「The VectorTile layer」」で使用した main.js のバックアップを保存して次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ cp main.js main.js_vectortile
user@deb9-vmw:~/openlayers-workshop-en$ vim main.js
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import MVT from 'ol/format/MVT';
import VectorTileLayer from 'ol/layer/VectorTile';
import VectorTileSource from 'ol/source/VectorTile';
import Overlay from 'ol/Overlay';
// See https://openmaptiles.com/hosting/ for terms and access key
const key = '<your-access-key-here>';
const map = new Map({
 target: 'map-container',
 view: new  View({
  center: [0, 0],
  zoom: 2
 })
});
const layer = new VectorTileLayer({
 source: new VectorTileSource({
  attributions: [
   '<a href="http://www.openmaptiles.org/" target="_blank">&copy; OpenMapTiles</a>',
   '<a href="http://www.openstreetmap.org/about/" target="_blank">&copy; OpenStreetMap contributors</a>'
  ],
  format: new MVT(),
  url: `https://free-{1-3}.tilehosting.com/data/v3/{z}/{x}/{y}.pbf.pict?key=${key}`,
  maxZoom: 14
 })
});
map.addLayer(layer); 
const overlay = new Overlay({
 element: document.getElementById('popup-container'),
 positioning: 'bottom-center',
 offset: [0, -10],
 autoPan: true
});
map.addOverlay(overlay);
overlay.getElement().addEventListener('click', function() {
 overlay.setPosition();
});
map.on('click', function(e) {
 let markup = '';
 map.forEachFeatureAtPixel(e.pixel, function(feature) {
  markup += `${markup && '<hr>'}<table>`;
  const properties = feature.getProperties();
  for (const property in properties) {
   markup += `<tr><th>${property}</th><td>${properties[property]}</td></tr>`;
  }
  markup += '</table>';
 }, {hitTolerance: 1});
 if (markup) {
  document.getElementById('popup-content').innerHTML = markup;
  overlay.setPosition(e.coordinate);
 } else {
  overlay.setPosition();
 }
});
http://localhost:3000/ とブラウザでマップを開きます。(もし開かなければ、'npm start' を実行してください。



OpenLayers5 Workshop - 3.1 The VectorTile layer

3 Vector Tile

Beautiful maps with vector tiles

In this module we will learn everything about the VectorTile layer in OpenLayers, and bring in the ol-mapbox-style utility to work with Mapbox Style files.

このモジュールでは、OpenLayers の VectorTile レイヤについてすべてを学習し、Mapbox Style ファイルで動作するために ol-mapbox-style ユーティリティを導入します。

● The VectorTile layer
● Interact with VectorTile features
● Styling a VectorTile layer
● Making things look bright

● VectorTile レイヤ
● VectorTile フィーチャを使ってインタラクション
● VectorTile レイヤをスタイルする
● ものを明るく見せる


3.1 The VectorTile layer
VectorTile レイヤ

We now know how to load tiled images, and we have seen different ways to load and render vector data. But what if we could have tiles that are fast to transfer to the browser, and can be styled on the fly? Well, this is what vector tiles were made for. OpenLayers supports vector tiles through the VectorTile layer.

現在、タイル化した画像をロードする方法を知っていて、ベクタデータをロードし描画する違う方法を確認します。しかし、ブラウザに転送するのが速い、そしてスタイルできるタイルがここにあるならどうしますか。ところで、これはベクタタイル向きです。OpenLayers は、VectorTile レイヤを通してベクタタイルをサポートします。

A world map rendered from vector data
ベクタデータから描画された世界地図

We'll start with the same markup in index.html as in the Basics exercise.

Basics 演習と同じ index.html で同じマークアップで始めます。
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>OpenLayers</title>
  <style>
   html, body, #map-container {
    margin: 0;
    height: 100%;
    width: 100%;
    font-family: sans-serif;
   }
  </style>
 </head>
 <body>
  <div id="map-container"></div>
 </body>
</html>
As usual, we save index.html in the root of our workshop folder.

いつもの通り、ワークショップフォルダのルートに index.html を保存します。

For the application, we'll start with a fresh main.js in the root of the workshop folder, and add the required imports:

アプリケーションのために、ワークショップフォルダのルートに新しい main.js で始め、必要とされる import を追加します:
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import MVT from 'ol/format/MVT';
import VectorTileLayer from 'ol/layer/VectorTile';
import VectorTileSource from 'ol/source/VectorTile';
The data source we are going to use requires an access key. Please read the terms at https://openmaptiles.com/hosting/, where you can also get your own key. The code below assigns the key to a constant we're going to use later:

使おうとしているデータソースは、アクセスキーが必要です。自身のキーも取得できる https://openmaptiles.com/hosting/ で項目を読みます。コードの下部にキーをあとで使う定数(const)に割り当てます:
// See https://openmaptiles.com/hosting/ for terms and access key
const key = '<your-access-key-here>';
The map we're going to create here is the same that we have used in previous exercises:

ここに作成するマップは、前回の演習で使われたのと同じです:
const map = new Map({
 target: 'map-container',
 view: new  View({
  center: [0, 0],
  zoom: 2
 })
});
The layer type we are going to use now is VectorTileLayer, with a VectorTileSource:

今、使うレイヤタイプは VectorTileLayer で、VectorTileSource と一緒に使います:
const layer = new VectorTileLayer({
 source: new VectorTileSource({
  attributions: [
   '<a href="http://www.openmaptiles.org/" target="_blank">&copy; OpenMapTiles</a>',
   '<a href="http://www.openstreetmap.org/about/" target="_blank">&copy; OpenStreetMap contributors</a>'
  ],
  format: new MVT(),
  url: `https://free-{1-3}.tilehosting.com/data/v3/{z}/{x}/{y}.pbf.pict?key=${key}`,
  maxZoom: 14
 })
});
map.addLayer(layer);
Our data source provides only zoom levels 0 to 14, so we need to configure custom tile grid. Vector tile layers are usually optimized for a tile size of 512 pixels, which we also configured with the tile grid. The data provider also requires us to display some attributions.

データソースは、ズームレベル 0 から 14 までだけ提供するので、カスタムタイルグリッドを設定する必要があります。ベクタタイルレイヤは、通常、512 ピクセルのタイルサイズに最適化され、タイルグリッドで設定もされます。データプロバイダ(提供者)も出典(attribution)を表示するために必要です。

As you can see, a VectorTileSource is configured with a format and a url, just like a VectorLayer. The MVT format parses Mapbox Vector Tiles.

見た通り、VectorTileSource は、VectorLayer のように、format と url で設定します。

The working example at http://localhost:3000/ shows an unstyled vector map like this:

http://localhost:3000/ で演習例は、このようなスタイルされていないベクタマップを表示します:

A world map without style

■□ Debian9 で試します■□
「The VectorTile layer」の例を表示します。「Vector Data」で使用した index.html のバックアップを保存して次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ cp index.html index.html_vectordata
user@deb9-vmw:~/openlayers-workshop-en$ vim index.html
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>OpenLayers</title>
  <style>
   html, body, #map-container {
    margin: 0;
    height: 100%;
    width: 100%;
    font-family: sans-serif;
   }
  </style>
 </head>
 <body>
  <div id="map-container"></div>
 </body>
</html>
「Vector Data」で使用した main.js のバックアップを保存して次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ cp main.js main.js_style
user@deb9-vmw:~/openlayers-workshop-en$ vim main.js
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import MVT from 'ol/format/MVT';
import VectorTileLayer from 'ol/layer/VectorTile';
import VectorTileSource from 'ol/source/VectorTile';
// See https://openmaptiles.com/hosting/ for terms and access key
const key = '<your-access-key-here>';
const map = new Map({
 target: 'map-container',
 view: new  View({
  center: [0, 0],
  zoom: 2
 })
});
const layer = new VectorTileLayer({
 source: new VectorTileSource({
  attributions: [
   '<a href="http://www.openmaptiles.org/" target="_blank">&copy; OpenMapTiles</a>',
   '<a href="http://www.openstreetmap.org/about/" target="_blank">&copy; OpenStreetMap contributors</a>'
  ],
  format: new MVT(),
  url: `https://free-{1-3}.tilehosting.com/data/v3/{z}/{x}/{y}.pbf.pict?key=${key}`,
  maxZoom: 14
 })
});
map.addLayer(layer); 
「MapTiler CLOUD」のアクセスキーを取得するためにホームページ(https://cloud.maptiler.com/)右上の「Sign in」をクリックします。
「Sign in or create account」ページのアカウント作成方法のうち、Google アカウントがあったので、「Sign in with Google」をクリックします。
Google のログインページが表示されるので、ログインします。
「Get started」ページが表示されるので、左側の「Account」をクリックします。
「Account」の「Settings」ページが表示されるので、左側の「key」をクリックして、「Key」を確認します。
const key = '<your-access-key-here>'; に key を入力します。

http://localhost:3000/ とブラウザでマップを開きます。(もし開かなければ、'npm start' を実行してください。



2018年9月26日水曜日

OpenLayers5 Workshop - 2.7 Making it look nice

2 Vector Data
2.7 Making it look nice
見栄えを良くする

At this point we have a feature editor with basic import, editing, and export functionality. But we haven't spent any time trying to make the features look nice. When you create a vector layer in OpenLayers, you get a set of default styles. The editing interactions (draw and modify) also come with their own default styles. You may have noticed how geometries had a thicker stroke during editing. This behavior can be controlled by providing a style option to your vector layer and editing interactions.

このポイントは、基本的なインポートと編集、エクスポート機能があるフィーチャエディタがあります。しかし、フィーチャの見栄えを良くさせる試みに全く時間を使っていません。OpenLayers でベクタレイヤを作成するとき、一連のデフォルトスタイルを取得します。editing インストラクション(draw と modify)も、それら自身のデフォルトスタイルを備えています。ジオメトリは編集中により太いストロークをどのように持っていたか気がついているでしょう。この動作は、ベクタレイヤにスタイルオプションと editing インストラクションを提供することによって制御されます。

Static style
静的スタイル

If we wanted to give all features the same style, we could configure our vector layer like this:

もしすべてのフィーチャに同じスタイルを与えるなら、ベクタレイヤをこのように設定します:
const layer = new VectorLayer({
 source: source,
 style: new Style({
  fill: new Fill({
   color: 'red'
  }),
  stroke: new Stroke({
   color: 'white'
  })
 })
});
It is also possible to set the style property to an array of styles. This allows rendering of a cased line (a wide stroke below and a narrower one on top), for example.

スタイルプロパティをスタイルの配列に設定することも可能です。例えば、台形ライン(下が広く上が狭いストローク)の描画を許可します。

While there isn't really a good justification of it here, for the sake of this exercise we'll take advantage of dynamic styling.

その良い根拠はここには本当にないですが、この演習のために、動的スタイリングの有利な点を使用します。

Dynamic style
動的スタイル

When you want to make decisions about how each feature should get rendered based on something about the feature or the current view resolution, you can configure a vector layer with a style function. This function gets called with each feature at each render frame, so it is important to write an efficient function if you have many features and want to maintain good rendering performance.

各々のフィーチャがフィーチャと現在のビューの投影法を元に描画される方法について決定するとき、ベクタレイヤをスタイル(style)ファンクションで設定できます。このファンクションは各々の描画フレームの各々のフィーチャで呼び出されるので、多くのフィーチャがあり良好な描画パフォーマンスを維持したいなら効率的なファンクションを書くことが重要です。

Here is an example that renders features using one of two styles depending on whether the "name" attribute starts with "A-M" or "N-Z" (a completely contrived example).

ここに "name" 属性が "A-M" または "N-Z" で開始するかによる2つのスタイルの1つを使用してフィーチャを描画する(全く不自然な)例があります。
const layer = new VectorLayer({
 source: source,
 style: function(feature, resolution) {
  const name = feature.get('name').toUpperCase();
  return name < "N" ? style1 : style2; // assuming these are created elsewhere
 }
});
Styling based on geometry area
ジオメトリエリアを元とするスタイリング

To see how dynamic styling works, we'll create a style function that renders features based on the geometry area. To do this, we're going to make use of a colormap package on npm. We can add this to our dependencies like this:

動的スタイリングがどのように動作するかを確認するために、ジオメトリエリアを元とするフィーチャを描画するスタイルファンクションを作成します。これを実行するために、npm の colormap パッケージを使用します。これを依存関係にこのように追加します:

npm install colormap

Now, we need to import the style constructors, the colormap package, and ol/sphere for spherical area calculations.

では、スタイルコンストラクタと colormap パッケージ、球面の計算のための ol/sphere をインポートします。
import {Fill, Stroke, Style} from 'ol/style';
import {getArea} from 'ol/sphere';
import colormap from 'colormap';
Next we'll write a couple functions to determine a color based on the area of a geometry:

次に、ジオメトリの面積をもととする色を決定する対のファンクションを書きます:
const min = 1e8; // the smallest area
const max = 2e13; // the biggest area
const steps = 50;
const ramp = colormap({
 colormap: 'blackbody',
 nshades: steps
});
function clamp(value, low, high) {
 return Math.max(low, Math.min(value, high));
}
function getColor(feature) {
 const area = getArea(feature.getGeometry());
 const f = Math.pow(clamp((area - min) / (max - min), 0, 1), 1 / 2);
 const index = Math.round(f * (steps - 1));
 return ramp[index];
}
And now we can add a function that creates a style with a fill color based on the geometry area. Set this function as the style property of your vector layer:

それでは、ジオメトリの面積をもとに塗りつぶし色(fill color)でスタイルを作成するのファンクションを追加できます。このファンクションをベクタレイヤのスタイルプロパティとして設定します:
const layer = new VectorLayer({
 source: source,
 style: function(feature) {
  return new Style({
   fill: new Fill({
    color: getColor(feature)
   }),
   stroke: new Stroke({
    color: 'rgba(255,255,255,0.8)'
   })
  });
 }
});
Features colored by area

■□ Debian9 で試します■□
「Static style」の例を表示します。前回使用した main.js のバックアップを保存して次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ cp main.js main.js_dawnload user@deb9-vmw:~/openlayers-workshop-en$ vim main.js
import 'ol/ol.css';
import GeoJSON from 'ol/format/GeoJSON':
import Map from 'ol/Map';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import View from 'ol/View';
import DragAndDrop from 'ol/interaction/DragAndDrop';
import Modify from 'ol/interaction/Modify';
import Draw from 'ol/interaction/Draw';
import Snap from 'ol/interaction/Snap';
import sync from 'ol-hashed';
import Style from 'ol/style/Style';
import Fill from 'ol/style/Fill';
import Stroke from 'ol/style/Stroke';
const map = new Map({
 target: 'map-container',
 view: new View({
  center: [0, 0],
  zoom: 2
 })
}); 
sync(map);
const source = new VectorSource();

const layer = new VectorLayer({
 source: source,
 style: new Style({
  fill: new Fill({
   color: 'red'
  }),
  stroke: new Stroke({
   color: 'white'
  })
 })
});
map.addLayer(layer);
map.addInteraction(new DragAndDrop({
 source: source,
 formatConstructors: [GeoJSON]
}));

map.addInteraction(new Modify({
 source: source
}));

map.addInteraction(new Draw({
 type: 'Polygon',
 source: source
}));

map.addInteraction(new Snap({
 source: source
}));
const clear = document.getElementById('clear');
clear.addEventListener('click', function() {
 source.clear();
});
const format = new GeoJSON({featureProjection: 'EPSG:3857'});
const download = document.getElementById('download');
source.on('change', function() {
 const features = source.getFeatures();
 const json = format.writeFeatures(features);
 download.href = 'data:text/json;charset=utf-8,' + json;
});
http://localhost:3000/ とブラウザでマップを開きます。(もし開かなければ、'npm start' を実行してください。


「Dynamic style」の例を表示します。main.js を次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ vim main.js
import 'ol/ol.css';
import GeoJSON from 'ol/format/GeoJSON':
import Map from 'ol/Map';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import View from 'ol/View';
import DragAndDrop from 'ol/interaction/DragAndDrop';
import Modify from 'ol/interaction/Modify';
import Draw from 'ol/interaction/Draw';
import Snap from 'ol/interaction/Snap';
import sync from 'ol-hashed';
import Style from 'ol/style/Style';
import Fill from 'ol/style/Fill';
import Stroke from 'ol/style/Stroke';
const map = new Map({
 target: 'map-container',
 view: new View({
  center: [0, 0],
  zoom: 2
 })
}); 
sync(map);

const source = new VectorSource();
/**
 * const layer = new VectorLayer({
 *  source: source,
 *  style: new Style({
 *   fill: new Fill({
 *    color: 'red'
 *    }),
 *   stroke: new Stroke({
 *    color: 'white'
 *   })
 *  })
 * });
 */
const layer = new VectorLayer({
 source: source,
 style: function(feature, resolution) {
  const name = feature.get('name').toUpperCase();
   return name < "N" ? style1 : style2;
 }
});
const style1 = new Style({
 fill: new Fill({
  color: 'red'
  }),
 stroke: new Stroke({
  color: 'white'
 })
});
const style2 = new Style({
 fill: new Fill({
  color: 'blue'
  }),
 stroke: new Stroke({
  color: 'yellow'
 })
});
map.addLayer(layer);
map.addInteraction(new DragAndDrop({
 source: source,
 formatConstructors: [GeoJSON]
}));

map.addInteraction(new Modify({
 source: source
}));

map.addInteraction(new Draw({
 type: 'Polygon',
 source: source
}));

map.addInteraction(new Snap({
 source: source
}));
const clear = document.getElementById('clear');
clear.addEventListener('click', function() {
 source.clear();
});
const format = new GeoJSON({featureProjection: 'EPSG:3857'});
const download = document.getElementById('download');
source.on('change', function() {
 const features = source.getFeatures();
 const json = format.writeFeatures(features);
 download.href = 'data:text/json;charset=utf-8,' + json;
});
http://localhost:3000/ とブラウザでマップを開きます。


「Styling based on geometry area」の例を表示します。 colormap パッケージをインストールします。

npm install colormap

main.js を次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ vim main.js
import 'ol/ol.css';
import GeoJSON from 'ol/format/GeoJSON':
import Map from 'ol/Map';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import View from 'ol/View';
import DragAndDrop from 'ol/interaction/DragAndDrop';
import Modify from 'ol/interaction/Modify';
import Draw from 'ol/interaction/Draw';
import Snap from 'ol/interaction/Snap';
import sync from 'ol-hashed';
import {Fill, Stroke, Style} from 'ol/style';
import {getArea} from 'ol/sphere';
import colormap from 'colormap'; 
const min = 1e8; // the smallest area
const max = 2e13; // the biggest area
const steps = 50;
const ramp = colormap({
 colormap: 'blackbody',
 nshades: steps
});
function clamp(value, low, high) {
 return Math.max(low, Math.min(value, high));
}
function getColor(feature) {
 const area = getArea(feature.getGeometry());
 const f = Math.pow(clamp((area - min) / (max - min), 0, 1), 1 / 2);
 const index = Math.round(f * (steps - 1));
 return ramp[index];
}
const map = new Map({
 target: 'map-container',
 view: new View({
  center: [0, 0],
  zoom: 2
 })
}); 
sync(map);

const source = new VectorSource();
/**
 * const layer = new VectorLayer({
 *  source: source,
 *  style: new Style({
 *   fill: new Fill({
 *    color: 'red'
 *    }),
 *   stroke: new Stroke({
 *    color: 'white'
 *   })
 *  })
 * });
 */
/**
 * const layer = new VectorLayer({
 *  source: source,
 *  style: function(feature, resolution) {
 *   const name = feature.get('name').toUpperCase();
 *    return name < "N" ? style1 : style2;
 *  }
 * });
 * const style1 = new Style({
 *  fill: new Fill({
 *   color: 'red'
 *   }),
 *  stroke: new Stroke({
 *   color: 'white'
 *  })
 * });
 * const style2 = new Style({
 *  fill: new Fill({
 *   color: 'blue'
 *   }),
 *  stroke: new Stroke({
 *   color: 'yellow'
 *  })
 * });
 */
const layer = new VectorLayer({
 source: source,
 style: function(feature) {
  return new Style({
   fill: new Fill({
    color: getColor(feature)
   }),
   stroke: new Stroke({
    color: 'rgba(255,255,255,0.8)'
   })
  });
 }
});
map.addLayer(layer);
map.addInteraction(new DragAndDrop({
 source: source,
 formatConstructors: [GeoJSON]
}));

map.addInteraction(new Modify({
 source: source
}));

map.addInteraction(new Draw({
 type: 'Polygon',
 source: source
}));

map.addInteraction(new Snap({
 source: source
}));
const clear = document.getElementById('clear');
clear.addEventListener('click', function() {
 source.clear();
});
const format = new GeoJSON({featureProjection: 'EPSG:3857'});
const download = document.getElementById('download');
source.on('change', function() {
 const features = source.getFeatures();
 const json = format.writeFeatures(features);
 download.href = 'data:text/json;charset=utf-8,' + json;
});
http://localhost:3000/ とブラウザでマップを開きます。


OpenLayers5 Workshop - 2.6 Downloading features

2 Vector Data
2.6 Downloading features
フィーチャをダウンロード

After uploading data and editing it, we want to let our users download the result. To do this, we'll serialize our feature data as GeoJSON and create an <a> element with a download attribute that triggers the browser's file save dialog. At the same time, we'll add a button to the map that let's users clear existing features and start over.

データをアップロードして編集した後、ユーザが結果をダウンロードできるようにします。これをするために、フィーチャデータを GeoJSON としてシリアル化し、ブラウザのファイル保存ダイアログをトリガ(動作開始)する download 属性と共う <a> エレメントを作成します。同時に、ユーザが既存のフィーチャを削除し、やり直すボタンをマップに追加します。

First, we need a bit of markup to represent the buttons. Add the following elements below the map-container in your index.html:

最初に、ボタンを表示する少しのマークアップが必要です。index.html の map-container の下に次のエレメントを追加します:
<div id="tools">
 <a id="clear">Clear</a>
 <a id="download" download="features.json">Download</a>
</div>
Now we need some CSS to make the buttons look right. Add something like this to the <style> element in index.html:

次に、右の方に見えるボタンを作るためのいくつかの CSS が必要です。 index.html にこのようなものを <style> エレメントに追加します:
#tools {
 position: absolute;
 top: 1rem;
 right: 1rem;
}
#tools a {
 display: inline-block;
 padding: 0.5rem;
 background: white;
 cursor: pointer;
}
Clearing features is the easier part, so we'll do that first. The vector source has a source.clear() method. We want clicks on the "Clear" button to call that method, so we'll add a listener for click in our main.js:

フィーチャの削除は簡単な部分なので、最初に行います。ベクタソース(vector source)は source.clear() メソッドがあります。メッソドを呼び出すために "Clear" ボタンをクリックしたいので、main.js に click のリスナを追加します:
const clear = document.getElementById('clear');
clear.addEventListener('click', function() {
 source.clear();
});
To serialize our feature data for download, we'll use a GeoJSON format. Since we want the "Download" button to work at any time during editing, we'll serialize features on every change event from the source and construct a data URI for the anchor element's href attribute:

ダウンロード用のフィーチャデータをシリアル化するために、GeoJSON フォーマットを使います。編集中いつでも動作する "Download" ボタンがほしいので、いつの change イベントでもソースからフィーチャをシリアル化し、アンカーエレメントの href 属性用のデータ URI を構築します:
const format = new GeoJSON({featureProjection: 'EPSG:3857'});
const download = document.getElementById('download');
source.on('change', function() {
 const features = source.getFeatures();
 const json = format.writeFeatures(features);
 download.href = 'data:text/json;charset=utf-8,' + json;
});
Buttons to clear and download data

■□ Debian9 で試します■□
前回使用した index.html のバックアップを保存して次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ cp index.html index.html_snap
user@deb9-vmw:~/openlayers-workshop-en$ vim index.html
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>OpenLayers</title>
  <style>
   html, body, #map-container {
    margin: 0;
    height: 100%;
    width: 100%;
    font-family: sans-serif;
    background-color: #04041b;
   }
   #tools {
    position: absolute;
    top: 1rem;
    right: 1rem;
   }
   #tools a {
    display: inline-block;
    padding: 0.5rem;
    background: white;
    cursor: pointer;
   }
  </style>
 </head>
 <body>
  <div id="map-container"></div>
  <div id="tools">
   <a id="clear">Clear</a>
   <a id="download" download="features.json">Download</a>
  </div>
 </body>
</html>
前回使用した main.js のバックアップを保存して次のように修正します。

user@deb9-vmw:~/openlayers-workshop-en$ cp main.js main.js_snap user@deb9-vmw:~/openlayers-workshop-en$ vim main.js
import 'ol/ol.css';
import GeoJSON from 'ol/format/GeoJSON':
import Map from 'ol/Map';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import View from 'ol/View';
import DragAndDrop from 'ol/interaction/DragAndDrop';
import Modify from 'ol/interaction/Modify';
import Draw from 'ol/interaction/Draw';
import Snap from 'ol/interaction/Snap';
import sync from 'ol-hashed';
const map = new Map({
 target: 'map-container',
 view: new View({
  center: [0, 0],
  zoom: 2
 })
}); 
sync(map);

const source = new VectorSource();

const layer = new VectorLayer({
 source: source
});
map.addLayer(layer);

map.addInteraction(new DragAndDrop({
 source: source,
 formatConstructors: [GeoJSON]
}));

map.addInteraction(new Modify({
 source: source
}));

map.addInteraction(new Draw({
 type: 'Polygon',
 source: source
}));

map.addInteraction(new Snap({
 source: source
}));

const clear = document.getElementById('clear');
clear.addEventListener('click', function() {
 source.clear();
});

const format = new GeoJSON({featureProjection: 'EPSG:3857'});
const download = document.getElementById('download');
source.on('change', function() {
 const features = source.getFeatures();
 const json = format.writeFeatures(features);
 download.href = 'data:text/json;charset=utf-8,' + json;
});
http://localhost:3000/ とブラウザでマップを開きます。(もし開かなければ、'npm start' を実行してください。

三角形を描画します。(地図は表示しません。そのデータもダウンロードされてしまいます。)


「Download」ボタンをクリックします。


ダイアログで「OK」ボタンをクリックします。


ファイルがダウンロードディレクトリに保存されます。


内容は次のようになていました。
{"type":"FeatureCollection","features":
 [{"type":"Feature","geometry":
  {"type":"Polygon","coordinates":
   [[
    [76.76796875,59.17592824927138],
    [56.02578125,40.97989806962016],
    [93.99453125,40.17887331434699],
    [76.76796875,59.17592824927138]
   ]]
  },
  "properties":null
}]}