Electron で地図を表示する無料で最短の道

# 概要

Electron で地図を表示するアプリを作ってみます。
ライブラリとして地図ライブラリは`leaflet`を、マーカークラスタのために`leaflet.markercluster`を使います。

それぞれのバージョンは以下のとおりです。

|ライブラリ|バージョン|
|---+---|
|electron|1.12.2|
|leaflet|1.6.0|
|leaflet.markercluster|1.4.1|

# 準備

## node と npm

[　ここからダウンロード　](https://nodejs.org/ja/)

# 最短の道

## ディレクトリを作成

ディレクトリを名前はなんでもいいので作って、そこに以下の内容の package.json をおきます。ディレクトリの名前はとりあえず le にしておきます。

```package.json
{
  "name": "le",
  "version": "1.0.0",
  "description": "Sample map application.",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "author": ""
}
```

## Electron をインストール

```sh
npm i electron --save
```

## アプリの作成

le の下に以下の3ファイルを作成します。

### index.html

```index.html
<!DOCTYPE html>
<html lang=zxx>
<link
    rel="stylesheet"
    href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css"
>
<script
    src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js"
></script>

<style>
html, body, #mapid { height: 100% }
body { margin: 0 }
</style>

<title>Electron-Leaflet</title>
<div id="mapid"></div>
```

### main.js

```main.js
const { app, BrowserWindow } = require('electron')

const
createWindow = () => {
	const mainWindow = new BrowserWindow(
		{	width			: 1000
		,	height			: 1000
		,	webPreferences	: {
				preload			: __dirname + '/preload.js'
			}
		}
	)

	mainWindow.loadFile( 'index.html' )
}

app.whenReady().then(
	() => {
		createWindow()
		app.on(
			'activate'
		,	() => !BrowserWindow.getAllWindows().length && createWindow()
		)
	}
)

app.on(
	'window-all-closed'
,	() => process.platform !== 'darwin' && app.quit()
)
```

### preload.js

```preload.js
window.onload = () => {
	const
	map = L.map( 'mapid' ).setView( [ 35.6825, 139.752778 ], 15 )
	L.tileLayer(
		'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
	).addTo( map )
}
```

## デバッグ

```sh
npm run start
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73493/f29f800d-b2eb-e669-aa17-9b053ea463ee.png)

## 勘所

```css
html, body, #mapid { height: 100% }
```
で地図を囲む全ての要素の height を 100% にしないと地図が表示されません。

```css
body { margin: 0 }
```
がないと、地図の左上にちょっと余白ができて、右下が切れてしまいます。

```js
L.map( 'mapid' ).setView( [ 35.6825, 139.752778 ], 15 )
```
で id が mapid の div に地図をつけています。
center と zoom の両方を指定しないと、地図が表示されません。

# 発展

## デフォルトのマーカー

デフォルトのマーカーを皇居に置いてみます。

* preload.js の onload の中に以下の内容を加えます。

```js
    L.marker( [ 35.6825, 139.752778 ] ).addTo( map ).bindPopup( 'Imperial Palace.' ).openPopup()
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73493/078e578c-ec02-4274-a7a1-b924b00610d3.png)

## divIcon を使ったマーカー

divIcon を使うと、マーカーのアイコンとして div を使えます。div なんでやりたい放題できます。とりあえず赤いぽっちにしてみます。

* index.html の `<style>`に以下の内容を加えます。

```css
.marker {
    text-align      : center
;   color           : white
;   font-size       : 16
;   border-radius   : 8px
;   box-shadow      : 8px 8px 8px rgba( 0, 0, 0, 0.4 )
}
.red {
    background      : red
}
```

* preload.js の onload の中に以下の内容を加えます。

```js
    const   redMarker = { icon: L.divIcon( { className: 'red marker', iconSize: [ 16, 16 ] } ) }
    L.marker( [ 35.6825, 139.752778 ], redMarker ).addTo( map ).bindPopup( 'Imperial Palace.' ).openPopup()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73493/9e126038-effa-6527-ad42-66629cfc42e1.png)


## マーカークラスターグループ

leaflet.markercluster をインストールすると、地図にたくさんマーカーを置くとき、近接するマーカーをまとめて表示するような機能を実現できます。
とりあえず5個のマーカーのクラスターグループを北の丸公園においてみます。

* index.html に以下の内容を加えます。

```html
<link
    rel="stylesheet"
    href="https://unpkg.com/leaflet.markercluster@1.4.1/dist/MarkerCluster.css"
>
<link
    rel="stylesheet"
    href="https://unpkg.com/leaflet.markercluster@1.4.1/dist/MarkerCluster.Default.css"
>
<script
    src="https://unpkg.com/leaflet.markercluster@1.4.1/dist/leaflet.markercluster.js"
></script>
```

* preload.js の unload の中に以下の内容を加えます。

```js
	map.addLayer(
		L.markerClusterGroup().addLayer(
			L.marker( [ 35.691, 139.752 ], redMarker )
		).addLayer(
			L.marker( [ 35.692, 139.752 ], redMarker )
		).addLayer(
			L.marker( [ 35.690, 139.752 ], redMarker )
		).addLayer(
			L.marker( [ 35.691, 139.753 ], redMarker )
		).addLayer(
			L.marker( [ 35.691, 139.751 ], redMarker )
		)
	)
```

5個まとめて表示されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73493/a0755319-e766-b42c-0073-93500bd19247.png)

5 と書いてあるクラスターにズームしてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73493/3f9a2a53-4c88-af36-4a74-4d43f2f87942.png)


## 余談

### z-index について

この記事では地図の上に他の HTML エレメントがのっていませんが、たとえばメニューみたいなものを上にのせたいというような場合が考えられます。この時 #mapid の z-index を 0 にするか、上にのせたいものの z-index を 400 以上にする必要があります。

```css
#mapid {
    z-index         : 0
}
```

### 警告について

View メニューから Toggle Developer Tools を選ぶと、HTML のデバッガが出てきて、console をみると以下のような警告が表示されています。

```
security-warnings.ts:179 Electron Security Warning (Insecure Content-Security-Policy) This renderer process has either no Content Security
    Policy set or a policy with "unsafe-eval" enabled. This exposes users of
    this app to unnecessary security risks.

For more information and help, consult
https://electronjs.org/docs/tutorial/security.
This warning will not show up
once the app is packaged.
```

これが気になる方は preload.js の頭で

```js
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = '1'
```

としてください。

もう一つの方法としてスタンドアローンアプリであれば、index.html に以下のように書けばいいのですが、Leaflet を使う場合はこれを書くと地図データをダウンロードできなくなります。

```html
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
```

### Leaflet 1.6.0

[前回の記事（Vue で地図を表示する無料で最短の道）](https://qiita.com/drafts/929a63e9b266f6db7958/edit)を書いた時に比べ Leaflet は少し書きやすくなりました。

また前回は npm でインストールしたのですが、いずれにしろネットワークが必ず必要なので今回は CDN にしてみました。


# 最後に

leaflet は機能豊富なライブラリなので、いくらでもネタはあるんですが、キリがないのでこのへんにしておきます。最後までおつきあいいただいてありがとうございました。