waigani's diary

QGISを中心にFOSS4Gをいじくる

地理院地図3Dにthree.jsのOculusRiftEffectとOculusControlsを足してみる

FOSS4G Advent Calendar 2014

今年もFOSS4G Advent Calendarが行われます。今年はQiitaに場所を移しての開催ですのでご注意ください。自分で参加日登録して、記事を書いたら自分でリンクすることになります。
FOSS4G Advent Calendar 2014 - Qiita

昨年の様子は下記からご覧ください。
FOSS4G Advent Calendar 2013 : ATND

以下の記事はAdvent Calendarのネタに使おうかと思いましたが、FOSS4G出てこないなと考え直して先に公開しちゃいます。

OculusRift風表示

夏に某所でthree.jsで簡単に試せるよと教えてもらったのですが、そのまま放置してたのでそろそろ試そうかと。
three.jsのexamplesに、OculusRiftのeffectとcontrolsの例が載っています。それを真似して、地理院地図3DからダウンロードできるWebGL用ファイルにeffectとcontrolsを追加してあげます。OculusRiftは持ってないので、ブラウザで表示までやってみましょう。

地理院地図3DからWebGL用ファイルをダウンロード

地理院地図3Dから、WebGL用のファイルをダウンロードしておきます。お好きな範囲で3Dモデルを作成しましょう。
ダウンロードしたファイルは解凍して、webサーバの元におきます。
f:id:waigani:20141108003736p:plain

three.jsから必要なファイルをコピー

three.js - Javascript 3D libraryをダウンロードします。
必要なファイルは解凍後の、以下の3ファイルです。

  • examples/js/effects/OculusRiftEffect.js
  • examples/js/controls/OculusControls.js
  • examples/js/controls/FirstPersonControls.js

これらのファイルを、地理院地図3DからダウンロードしたWebGL用ファイルのディレクトリにコピーしておきます。js以下に置くとしました。

  • js/OculusRiftEffect.js
  • js/OculusControls.js
  • js/FirstPersonControls.js

地理院地図3Dを少々修正

下記の箇所を追加・修正しています。

  • 16-18行目 必要なファイルを追加
  • 23-25行目 追加したcontrolsのupdate追加
  • 28-31行目 OculusRiftEffectでの描画に変更
  • 134行目 TrackballControlsをコメントアウト
  • 137-139行目 OculusRiftEffectの作成
  • 140-141行目 OculusControlsの作成
  • 143-149行目 FirstPersonControlsの作成
  • 164-165行目 OculusRiftEffectへのサイズ設定


表示してみると

こんな感じで表示されます。キーボードからw/s/a/d/r/fキーで操作することも出来ます。
f:id:waigani:20141108093042p:plain
f:id:waigani:20141108093050p:plain

TaoVisor | 3DVRゴーグル タオバイザー着たらちゃんと遊ぼう。

国土地理院のベクトルタイルをQGISに読み込んでみる

ベクトルタイルの提供実験

国土地理院のベクトルタイルの提供実験が行われています。
ベクトルタイルの提供実験について|国土地理院

早速、D3.js+leafletで表示している方も。
[D3+Leaflet]国土地理院さん渾身のベクトルタイルで遊んでみた。 | GUNMA GIS GEEK

乗り遅れるわけにはいかないので、QGISで強引に表示してみます。

今回試してみた方法

道路中心線のベクトルタイルはGeoJSONで提供されています。QGISでは一般的なベクトルデータを読み込む際は、OGRというベクトルデータを扱うライブラリを利用しています。OGRはGeoJSONにも対応していますので、QGISでGeoJSONをそのまま読み込むことが出来ます。

試しに提供されているベクトルタイルを1枚ダウンロードしておいて、QGISに表示してみましょう。
http://cyberjapandata.gsi.go.jp/xyz/experimental_rdcl/15/29132/12853.geojson

特に気をつける点もなく表示されます。
f:id:waigani:20140809151003j:plain

キャンパスの地理座標範囲からタイルファイルのズームレベル、X、Yを求めて、必要なファイルさえわかれば、後はGeoJSONにネット越しに直に、もしくは一旦ダウンロードしておいて表示することが出来ます。もちろん、キャンパスの縮尺の変更、表示範囲の変更に合わせて、ファイルの読み込み直しは必要になります。

今回は、キャンバスがリフレッシュされるタイミングで必要なタイルを計算して、1ファイルを1レイヤとして読み込んでみました。
拾うイベントがこれでいいのかとか、グループ消された時の処理とか、プラグイン消された時の処理とか色々考えるべきですが、まずは表示するというところをやってみたということで。

作ったプラグイン

プラグイン

GitHubに置いておきます。ユーザーディレクトリ\.qgis2\python\pluginsにコピーしてお試し下さい。
https://github.com/waigania13/vectorTileswaigania13/vectorTiles · GitHub

実行

【プラグイン】メニューに【vector tiles】→【国土地理院ベクトルタイル提供実験】が出来ますので、実行してみてください。
f:id:waigani:20140809154940j:plain

表示

つくば周辺に移動してベクトルタイルが表示されます。
f:id:waigani:20140810224101j:plain

注意

ogrではgeojsonへのupdateをサポートしてない旨、エラーがいっぱい出ますので注意してください。

処理内容

ズームレベルを求める

少々手抜き。キャンパス上の縮尺500をズームレベル20と仮定して、なんとなく求めます。

X,Yを求める

こんな関数を用意しておきました。この辺はタイルマップの説明をググっていただくと、色々資料があるかと。

vectortiles.py

主な処理を行っているファイルの中身です。

  • 102-103行 : キャンバスの表示範囲を経緯度で持って来ます
  • 106-108行 : 南西、北東のズーム、X、Yの決定
  • 112-119行 : 必要なファイルを決める、すでに読み込まれている時は改めて読み込まない
  • 132-139行 : 必要なタイルの読み込み
  • 143行 : 前回表示範囲のファイルと、今回の表示範囲のファイルを比較、必要無いレイヤは消す

とかやってます。
レイヤへのファイルの読み込みは、ネット越しに直に行っています。
124-130行のコメントアウトを外して、132行目をコメントアウトすると、ファイルを一時フォルダにダウンロードしてきた上で読み込みになります。


つづきあるかな...

別々のレイヤとして読まずに1つのレイヤに入れた方がスタイル付けるの楽ですよね。OpenLayers Pluginを改造した方が賢いかな。誰か任せた。

QGIS プロセッシング Scriptsで一括処理したい その2

若干捕捉

QGIS プロセッシング Scriptsで一括処理したい その1 - waigani's diaryの続きを少々。

multiple vector

入力にfolderを指定しているのですが、本当はファイルを複数指定したいところです。

##input=multiple vector

という指定方法もあるのですが、これだとQGISに読み込み済みのレイヤしか対象に出来なかったりします。

processing.getObject()

前回の記事とは関係ないのですが、processingの仕様もバージョンによって少し変わっています。ファイル名を受け取りレイヤインスタンスを返してくれたメソッドが、以前のバージョンですと、

processing.getobject()

と書いていたところが、QGIS 2.4では

processing.getObject()

となっていたりします。ご注意ください。

複数のベクトルファイルを1つにする

QGISのメニューに【ベクタ】→【データマネジメントツール】→【複数のシェープファイルを1つに結合する】という機能があります。processingで対応するのは'qgis:mergevectorlayers'になるようです。ただしprocessingで実行すると、2つのファイルを指定して1つにする機能になっているようです。
複数のベクトルファイルを指定して1つにマージする機能が、processingのアルゴリズムの中になさそうなので、自作してみましょう。
入力は、

  • 基準とするベクトルファイル指定
  • マージするベクトルファイルの入っているフォルダを指定
  • 出力ファイル名指定

とします。
処理の内容としては、

  • 基準のベクトルファイルを出力ベクトルファイルにコピー
  • 入力先フォルダから拡張子.shpのファイル名を取得
  • 基準のベクトルファイルと違う図形タイプのファイルは対象外
  • 基準のベクトルファイルと違う属性定義(名称と型だけ確認している)のファイルは対象外
  • 対象のファイルについては、出力ベクトルファイルに図形をコピー

としています。

##[My Scripts]=group
##source=vector
##target_folder=folder
##output_file=output vector

from processing.core.VectorWriter import VectorWriter
from processing.core.ProcessingLog import ProcessingLog
import os
import glob 

def isSameFields(f1, f2) :
    if f1.count() != f2.count() :
        return False
    for i in range(0, f1.count()) :
        if f1.at(i).name() != f2.at(i).name() :
            return False
        if f1.at(i).type() != f2.at(i).type() :
            return False

ProcessingLog().addToLog(ProcessingLog.LOG_INFO, "source vector file :" + source)
ProcessingLog().addToLog(ProcessingLog.LOG_INFO, "target folder :" + target_folder)
ProcessingLog().addToLog(ProcessingLog.LOG_INFO, "output vector file :" + output_file)

sourceLayer = processing.getObject(source)
provider = sourceLayer.dataProvider()

writer = VectorWriter(output_file, provider.encoding(), provider.fields(), provider.geometryType(), sourceLayer.crs())
features = sourceLayer.getFeatures()
for feature in features:
    writer.addFeature(feature)
  
files = glob.glob(os.path.join(target_folder, r'*.shp'))
for file in files:
    if os.path.normpath(file) == os.path.normpath(sourceLayer.source()) :
        continue
    layer = processing.getObject(file)
    if provider.geometryType ()  != layer.dataProvider().geometryType () :
        ProcessingLog().addToLog(ProcessingLog.LOG_INFO, "different geometry type :" + file)
        continue
    if isSameFields(provider.fields(), layer.dataProvider().fields()) :
        ProcessingLog().addToLog(ProcessingLog.LOG_INFO, "different fields :" + file)
        continue
    features = layer.getFeatures()
    for feature in features:
        writer.addFeature(feature)

del writer

適当な名前にして、

 ユーザのホームディレクトリ\.qgis2\processing\scripts

に置いておきます。qgisを再起動して、プロセッシングツールボックスから追加されたスクリプトを探して実行しましょう。

出力ベクトルファイルに対して、同一のスクリプト内もしくはモデラーで繋げて処理を行うことで、

  • ファイルをマージ
  • マージしたファイルに対して解析処理

の流れを作ってあげることが出来ます。

QGIS プロセッシング Scriptsで一括処理したい その1

QGIS プロセッシング Scripts

久々更新なのですっかり使い方忘れてます。QGIS プロセッシング Scriptsについては、下記の記事をまずはご参照ください。

QGIS プロセッシング Scriptsを使いたい その1 - waigani's diary

QGIS プロセッシング Scriptsを使いたい その2 - waigani's diary

QGIS プロセッシング Scriptsを使いたい その3 - waigani's diary

QGIS プロセッシング Scriptsを使いたい その4 - waigani's diary

フォルダ内のファイルをががーっと処理したい

フォルダ内にいくつかのshpefileがあるとして、同一の処理を全てのファイルに行うことを考えます。

下記のように、ランダムにポイントが配置されたshapefileが、それぞれの領域は重ならずに4ファイルあるとします。

f:id:waigani:20140723230024p:plain

 それぞれのファイルにボロノイ分割を行います。使用する機能としては、メニューの【ベクタ】→【ジオメトリツール】→【ボロノイポリゴン】になります。processingから呼ぶ際の名称としては、'qgis:voronoipolygons'です。

processing scriptsで処理するとして、まず入力は、

  • 入力先のフォルダを指定
  • ボロノイ図作成に必要なオプションを指定
  •  出力先のフォルダを指定

とします。

処理の内容としては、入力値のチェックなどなど省いて必要最小限にして、

  • 入力先フォルダから拡張子.shpのファイル名を取得
  • ファイル名からパス部分を削除して、出力先フォルダ名と'output_'を頭に付けて出力ファイル名作成
  • processingを使って、'qgis:voronoipolygons'を実行

だけ書いてみます。log出力してるのは気にしない。最後の行は、グラフィカルモデラーでつないでいくときのおまじない程度に考えてください。

##[My Scripts]=group
##input_dir=folder
##buffer=number 0.000000
##output_dir=folder
##outputDir=output string

from processing.core.ProcessingLog import ProcessingLog
import os
import glob

ProcessingLog().addToLog(ProcessingLog.LOG_INFO, "input folder :" + input_dir)
ProcessingLog().addToLog(ProcessingLog.LOG_INFO, "output folder :" + output_dir)

files = glob.glob(os.path.join(input_dir, r'*.shp'))
for file in files:
outputFile = os.path.join(output_dir, 'output_'+os.path.basename(file))
processing.runalg('qgis:voronoipolygons', file, buffer, outputFile)

outputDir = output_dir

"voronoi.py"という名称にして、

 ユーザのホームディレクトリ\.qgis2\processing\scripts

に置いておきます。qgisを再起動して、プロセッシングツールボックスから追加されたスクリプトを探して実行しましょう。

f:id:waigani:20140723233406p:plain

入力先フォルダ、出力先フォルダを指定して『run』すると、出力先フォルダにshapefileが作成されます。qgisでshapefileを開いてみると、ボロノイ図が作成されていることを確認出来ます。

f:id:waigani:20140723233756p:plain

改良点

バラバラにボロノイ図作っているので隙間開いてますね。読み込んだファイルを1ファイルにしてとか入れましょうか。

Mapboxを使ってみよう その2

前回

マーカーを1つ追加しての公開

Mapboxを使ってみよう その1 として、地図デザインを変更、マーカーを追加して公開するところまで行ってみました。

今回は、手持ちのベクトルデータがあったとして、アップロードして地図に追加してみます。

手持ちのベクトルデータを加えてみる

データの準備

geojson,csv,kml,gpxであれば、簡単に地図に追加することが出来ます。

ここでは

  • 国土数値情報からバス停留所のデータを取得
  • qgisでgeojsonに変換

としてみます。

国土数値情報ダウンロード

国土数値情報 からは、様々な国土に関する空間情報データをダウンロードすることが出来ます。データ形式としてJPGIS2.1を選択したまま、バス停留所を選択してください。

f:id:waigani:20140420163620p:plain

ダウンロードは都道府県単位で行えますので、北海道を選択して進みます。

JPGIS形式となっていますが、ダウンロードしたzipファイルを解凍すると、shapefileも入っています。shapefileの方を使用します。

f:id:waigani:20140420164108p:plain

QGISにて変換

QGISを起動して、ダウンロードしたバス停留所のshapefileを開きます。

全てのデータをアップロードするのは無理なため、一部必要なデータのみを切り取ることにします。無料プランですと、20点が限度になります。

下図の黄色い点が選択されている点とします。メニューから"レイヤ"→"選択部分をベクタファイルとして保存する"を選択し、geojson形式で名前を付けて保存しておきます。

f:id:waigani:20140420164752p:plain

データのインポート

さて、Mapbox上のprojectにベクトルデータを追加してみましょう。データを追加したいprojectを開いておいて下さい。

開いたら、先ほど保存しておいたgeojsonをドラッグ&ドロップで地図上に持って行きましょう。これだけでアップロードすることが出来ます。

f:id:waigani:20140420165313p:plain

Mapboxに読み込めるファイルだと認識されると、ポップアップに表示するタイトル、説明、スタイル、シンボルを設定するパネルが表示されます。タイトル、説明では、geojson中のどの属性を表示するかを選択出来ます。予めgeojsonの中をテキストエディタで確認して、どの属性を表示するか確認しておいた方がいいでしょう。

f:id:waigani:20140420165920p:plain

設定が終わったら、"Finish Importing"をクリックしましょう。

地図上にマーカーが追加されます。

f:id:waigani:20140420170414p:plain

出所の明記

「『国土数値情報(○○データ) 国土交通省』を使用して作成」と、出所の明記をする必要がありますので、お忘れなく。

 

Mapboxを使ってみよう その1

Mapboxとは?

Mapbox

ホームページに拡大縮小・スクロールといった操作が出来る地図を貼り付けたい、という時に真っ先に思いつくのはGoogle Mapでしょうか?確かにお手軽ですし、とても便利なサービスです。でもよく使われているサービスだけに、見た目の違いで差を付けるのはなかなか大変になります。地図のデザインを変更することも出来ますが、JavaScriptの知識は必要ですね。

もう少し手軽にオリジナルな地図を公開したいならMapboxがオススメです。

Mapboxは、簡単な操作でオリジナルな地図を作成、公開出来るサービスです。

ベースとなる地図はOpenstreetMapを使用するため、著作権を気にすることもありません。地図デザインを自分好みに変更し、目的に応じてマーカー、ライン、ポリゴンで情報を書き足し、書き足した図形へのポップアップ動作も付けてくれます。作成した地図はMapbox上に保存しておき、指定されたコードで自分のホームページへ貼り付けることが出来ます。

さらに開発者向けには、iOS用のSDKや、leafletを元にしたJavaScriptのライブラリも用意されていますので、作成した地図を様々な用途で使用することが出来ます。

f:id:waigani:20140418224536j:plain

プランは色々あるので気をつけよう

 こんな便利なMapboxは無料で使いはじめる事が出来ます。3,000map view/month、50MBの容量等の条件を超えてくると有料プランを使用する必要がありますので、注意はしましょう。plansのページを参照のこと。

Mapboxを使ってみよう

Sign up

まずはアカウントを作成しますが、詳細は省略。

f:id:waigani:20140418231413p:plain

Projectを作る

アカウントを作成し、Sign inしたら、Projectsを選択します。

projectが作成済みの場合は、タイトル一覧が表示されますが、最初のSign inなので空ですね。Create projectをクリックしてprojectの作成を開始します。

f:id:waigani:20140418232445p:plain

 表示位置を調整

エディタが起動すると、いきなり南大西洋辺りを中心に、アフリカ・南アメリカといった地域の地図が表示されます。このままだとデザインをしていくのも不便ですので、お目当ての地域を表示しましょう。GoogleMapと同じようなマウス操作でのドラックスクロール、マウスホイールでの拡大・縮小が出来ます。

この後の工程で位置を変更しても問題ありませんので、デザインしやすそうな位置にしておいてください。

f:id:waigani:20140418233726p:plain

Styleの調整

左上のメニューからStyleを選択します。

Color

色設定のパネルです。

  • Streets : 道路
  • Bdgs : 建物
  • Areas : 敷地
  • Water : 水部
  • Land : 土地

に対して、描画色をそれぞれ設定出来ます。

下図ですと、Streetsに対して"#3fbfbf"が設定されたRGB値となっていますが、この値に対してH(色相)、S(彩度)、L(輝度)が掛けられているので注意してください。"#3fbfbf"をそのまま表示したい場合は、H/S/Lを全て100%としてください。Aは透過の設定になります。

f:id:waigani:20140418234404p:plain

Discard paletteをクリックすると、予め用意されているいくつかのパターンから表示色を選択することが出来ます。

f:id:waigani:20140418235340p:plain

パターンや個別の表示色を変更して、好みの地図デザインを見つけてみてください。

Baselayer

 背景の地図に使うデータを選べます。

  • Streets : 最初に表示されている地図
  • Terrain : 地形
  • Satellite : 衛星画像

が選べますが、Satelliteは有料プランのみでの使用となります。ご注意下さい。

f:id:waigani:20140418235905p:plain

StreetsとTerrainを入れ替えて、どちらのデザインが目的の地図に合っているか選んでみましょう。

Language

地図中に表示される文字列(注記)の言語を設定出来ます。ここでは特に変更の必要はありません。

f:id:waigani:20140419000252p:plain

マーカーを置く

 メニューからData→Markerを選択します。

f:id:waigani:20140419000830p:plain

地図上でマウスカーソルが"✚"で表示されるようになります。マーカーを置きたい位置をクリックしましょう。マーカーが表示されます。

f:id:waigani:20140419001257p:plain

また、パネルの表示も変わり、ポップアップ時に表示されるテキストを入力することが出来ます。入力したテキストは随時マーカーのポップアップに反映されて表示されますので、確認してみてください。下部のタブを切り替えて、マーカーの色、マーカーに表示されるシンボル、経緯度の微調整も行えます。

f:id:waigani:20140419001748p:plain

一度置いたマーカーのテキスト、位置等を変更したい場合は、再度Data→Markerを選択している状態とした上で、配置済みマーカーをクリックします。パネル上で表示内容を変更出来ますし、マーカーをドラックすることで位置を変更することも出来ます。

projectを保存

 デザインが終わったところでprojectを保存しますが、保存の前にprojectに名前を付けておきましょう。メニューからProject→Settingsを選択します。わかりやすい名前を付けておいて下さい。

f:id:waigani:20140419003046p:plain

 

名前を付けたらSaveを選択し、projectを保存します。

公開する

 再度メニューからProjectを選択します。先ほどと変わってInfoの表示が下図のようになっています。

ここでShare linkに表示されているURIをブラウザからアクセスすると、作成した地図を見ることが出来ます。

Embedに表示されているのが、ホームページ等に貼り付ける際のコードになります。これをコピーしてホームページ上の好きな位置に作成した地図を入れることが出来ます。

f:id:waigani:20140419003656p:plain

ブログに貼り付ける

こんな感じになります

 

10.QGIS 2.0の日本語対応が大好きだ!

QGIS 2.0の日本語対応に尽力してくれた皆様本当にありがとうございます。
皆様のお陰で快適に使わせて頂いております。
日本語対応のため各種ソースをcommitしていただいている赤木様、メニュー日本語化をして頂いている嘉山様、GDALの日本語対応をしていただいた沖様、その他関係者皆様、本当にありがとうございます。
QGISハックフェスタに参加されたGeoRepublicの皆様、羨ましい!

当然、QGIS開発コミュニティには大感謝です。
QGISに寄付するという手もありますので、みなさん何らかの貢献を致しましょう。
QGIS Gold Sponsorのアジア航測様ありがとうございます。

来年は私も少しでも貢献出来るように致します。

長文にお付き合いいただけありがとうございました。