目次

simplekml

simplekmlは、PythonでKMLを作成するパッケージ。

KMLについて

KMLは、地理空間情報を記述するための書式の1つ。要は地図上にマーカー設置したりライン引いたりバルーンで情報表示したりできる。

これを読み取れる主要なアプリに、Google Earthがある。

Google EarthはGoogle MapsのPCアプリ版みたいなもので、衛星写真の地図をぐりぐり動かしたり拡大縮小できる。 KMLを用いると、その上にマーカーを置いたりラインを引いたりできる。

Google Earthは無償で利用できる上、数千件のデータを配置しても描画が高速。かなりの倍率までズームできるため、全体の概観から詳細の確認までできて便利。

(ただし、衛星写真をつなぎ合わせている関係上、使う写真の撮影時期や条件によって悪い箇所では見た目と10m規模の誤差は出るので、あくまで目安にとどめる)

インストール

> pip install simplekml

パッケージ管理ツールにAnacondaを使う場合は、標準レポジトリにないので、conda-forgeなどからインストール。

> conda install -c conda-forge simplekml

とりあえず使ってみる

とりあえず点や線を描いたりスタイルを変えるのは、これを使えば簡単にできる。

それ以上のことは、そもそもKMLでどこまでのことが出来るのか自体をイマイチ知らないんだけど、ドキュメントを見る限りいろんなことが出来る模様。

せっかくプログラム的に書けるので、連続した緯度経度座標を一気にプロットするクラスを作ってみた。

import simplekml


class PlotKml:
    # スタイルは適当に、simplekmlやKMLのドキュメントを見つつ好みに変更する
    # マーカーのスタイル1
    STYLE_I = simplekml.Style()
    STYLE_I.iconstyle.icon.href = 'http://maps.google.com/mapfiles/kml/shapes/placemark_circle_highlight.png'
    STYLE_I.labelstyle.scale = 0.66
    STYLE_I.labelstyle.color = 'aaffffff'
    # マーカーのスタイル2
    STYLE_H = simplekml.Style()
    STYLE_H.iconstyle.icon.href = 'http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png'
    STYLE_H.labelstyle.scale = 0.66
    STYLE_H.labelstyle.color = 'aaffffff'
    # ラインのスタイル
    STYLE_LS = simplekml.Style()
    STYLE_LS.linestyle.width = 3


    def __init__(self):
        self.kml = simplekml.Kml()

    def new(self):
        self.kml = simplekml.Kml()

    def draw_dots(self, lnglats, descriptions, style=None, linestring=True):
        """
        :param lnglats: [[lng, lat], [lng, lat], [], ...]
        :param descriptions: <name>属性に出力するタグ
        :param style: simplekml.Style のインスタンスまたはそのリスト
        :param linestring: 線が不要ならFalse
        """
        # descriptionsは、lnglatsと同じ要素数の文字列のリストで与える。
        #   対応する順番の文字列が、自動的に連番prefixを付与され、マーカーの横に添えられる
        # styleは、Noneなら全てデフォルト
        #   simplekml.Styleの(単独の)インスタンスの場合は全てそのスタイルに
        #   リストの場合はlnglatsと同じ要素数にする。対応する順番のマーカーがそのスタイルになる

        if descriptions is None:
            descriptions = [''] * len(lnglats)
        else:
            assert len(lnglats) == len(descriptions)

        if style is None:
            style = [self.STYLE_I] * len(lnglats)
        elif type(style) == simplekml.Style:
            style = [style] * len(lnglats)
        elif type(style) == list:
            assert len(lnglats) == len(style)

        for idx, (lnglat, description) in enumerate(zip(lnglats, descriptions)):
            pnt = self.kml.newpoint(name=str(idx) + ' ' + description, coords=[lnglat])
            pnt.style = style[idx]

        if linestring:
            ls = self.kml.newlinestring(name='LineString', coords=lnglats)
            ls.style = self.STYLE_LS

    def save(self, path):
        self.kml.save(path)

# --使い方--
pk = PlotKml()
pk.draw_dots([[135.26, 34.32], [135.23, 34.29], ...],  # lnglats
             ['2018/01/21', '2018/01/22', ...])        # descriptions
pk.save('out.kml')

pk.new()  # リセット(これをしないと前のが残る。敢えて残すことも可)
pk.draw_dots([[-112.35, 42.89], ...], style=PlotKml.STYLE_H)
pk.save('out2.kml')

Tips

Styleは使い回す

simplekml.Style クラスは、Pointのアイコンやサイズ・LineStringやPolygonの太さや色・ラベルの色などを規定するものである。

適用するには、点やラインなどの個々のインスタンスに.style属性があるので、そこに指定する。 個々のインスタンスは、kml.newpoint(), kml.newlinestring() などで作成すれば返値で受け取れる。

この時、以下の2通りの方法がある。

同じStyleを適用する地物をいくつも量産するのであれば、後者の方がよい。前者だと、地物毎に新しいStyleが定義され、KMLのサイズが肥大化する。

kml = simplekml.Kml()

# その場で定義
for _ in range(100):
    pnt = kml.newpoint(name='test1', coords=[[0, 0]])
    pnt.style.iconstyle.icon.href = 'http://icon/img/url.png'  # アイコン画像
    pnt.style.labelstyle.color = simplekml.Color.blue  # ラベル文字色
# => 全ての点でStyleインスタンスが別々になる(出力KMLで、Style定義が100回繰り返される)

# 最初に定義してから与える
style = simplekml.Style()
style.iconstyle.icon.href = 'http://icon/img/url.png'  # アイコン画像
style.labelstyle.color = simplekml.Color.blue  # ラベル文字色

for _ in range(100):
    pnt = kml.newpoint(name='test2', coords=[[0, 0]])
    pnt.style = style
# => Styleインスタンスは1つのみ(出力KMLで、1回のStyle定義を各地物で使い回す)

LineStringにラベルを表示

通常、KMLではLineStringにラベルは表示されないが、<gx:labelVisibility>1</gx:labelVisibility> をLineのStyleに記述することで、そのname属性が表示されるようになる。

これをsimpleKmlから利用するには、LineStyleに gxlabelvisibility = 1 を指定すればよい。

ホバー(マウスオーバー)によってスタイルを変える

生KMLの記載方法は上記サイトまたはKML参照。

要は、通常用とホバー時用、2個のStyleを定義して、それらを囲った「StyleMap」を定義し、それぞれのStyleに「normal」「highlight」というキーを与える。 このStyleMapのIDを、各Point, Lineなどに適用するスタイルとして与えてやればよい。

これは、simpleKmlでは以下のようにできる。

# ホバーによって太さと色が変わるLineString

STYLE_NORMAL = simplekml.Style()
STYLE_NORMAL.linestyle.width = 3
STYLE_NORMAL.linestyle.color = simplekml.Color.white
STYLE_HIGHLIGHT = simplekml.Style()
STYLE_HIGHLIGHT.linestyle.width = 5
STYLE_HIGHLIGHT.linestyle.color = simplekml.Color.red
STYLE_MAP = simplekml.StyleMap(normalstyle=STYLE_NORMAL, highlightstyle=STYLE_HIGHLIGHT)

kml = simplekml.Kml()
ls = kml.newlinestring(name='test', coords=[[0, 0], [1, 1]])
ls.stylemap = STYLE_MAP

kml.save('test.kml')

ファイルサイズを抑える

保存時、save() でなく savekmz() を使用すると、kmlをzip圧縮した規格であるkmzファイルが生成され、容量の節約になる。

クリックで開くバルーンを仕込む

Point, LineString, Polygonなどについて、クリックすると開いてより詳細な情報を表示するバルーンを設定できる。

なんか方法が3つほどあるらしい。「ExtendedData」を使うのが簡単。「Scheme」を使うと決まった型を定義できてデータ的に扱いやすい?らしい。

ここではExtendedDataの方法を扱う。KMLを記述する方法とその結果は、上記のリンク先参照。

simplekmlでは以下のように記述すれば出来る。name で項目名(str限定)、value で任意の値を埋め込める。

# LineStringをクリックすると3つほどの要素を表示させる

kml = simplekml.Kml()
ls = kml.newlinestring(name='test', coords=[[0, 0], [1, 1]])

ex_data = simplekml.ExtendedData()
ex_data.newdata(name='item1', value=3.14)
ex_data.newdata(name='item2', value='ITEM2')
ex_data.newdata(name='item3', value=[0, 1, 2])
ls.extendeddata = ex_data

kml.save('test.kml')