🌍

geojsonからハイライト機能のあるKMLを作成する。

2023/07/25に公開

何をするのか

pythonを使用して、geojsonに入力されているPolygonのデータをGoogleEarthProで表示できるKMLファイルに変換します。
しかし、ただ変換するだけでは面白くないので、マウスカーソルを当てたPolygonがハイライトされる様に変更します。

はじめに

私の職場ではArcGIS系のサービスを使用しているので、GISのデータはArcGIS系のWebアプリで可視化しています。業務の内容もあり季節ごとの衛星画像が好まれる様で
「GoogleEarthでも見れるようにしてくれ」
といつも言われているため、geojsonのデータをKMLに変換する作業をすることになりました。

GoogleEarthにいくつかのFeatureを表示させ、色を変更しようとすると、なぜだか選択していないFeatureも同時に色が変更されてしまい、とてもウザかったのでpythonで事前に色を設定し終えたKMLファイルを作成する事にしました。

今回使用するファイル

テストの為にいくつかのPolygonを用意しました。

  • ドーナツ型のPolygon
  • 普通のPolygon
  • MultiPolygon(3つ)
  • MultiPolygon(2つ)
sample.geojson
{
    'type': 'FeatureCollection',
    'features': [
        {
            'type': 'Feature',
            'id': 1,
            'geometry': {
                'type': 'Polygon',
                'coordinates': [
                    [
                        [140.84784088544723, 40.85011698995831],
                        [140.84625424685643, 40.845683781800616],
                        [140.85390155725938, 40.846656950195815],
                        [140.853072502982, 40.85153339192223],
                        [140.84784088544723, 40.85011698995831]
                    ],
                    [
                        [140.8521005078783, 40.847359785904125],
                        [140.8484984109127, 40.84761929226497],
                        [140.84945611112528, 40.84926280940356],
                        [140.85205762589985, 40.84953312139549],
                        [140.8521005078783, 40.847359785904125]
                    ]
                ]
            },
            'properties': {'OBJECTID': 1, 'Shape_Length': 4160.542989475683, 'Shape_Area': 386695.8680073871}
        },
        {
            'type': 'Feature',
            'id': 2,
            'geometry': {
                'type': 'Polygon',
                'coordinates': [
                    [
                        [140.85520231419758, 40.85352279031235],
                        [140.85543101898068, 40.850160239176134],
                        [140.86103428122664, 40.85046298426954],
                        [140.860862753313, 40.85392282529552],
                        [140.85520231419758, 40.85352279031235]
                    ]
                ]
            },
            'properties': {'OBJECTID': 2, 'Shape_Length': 2263.2659259921943, 'Shape_Area': 315889.0727321759}
        },
        {
            'type': 'Feature',
            'id': 3,
            'geometry': {
                'type': 'MultiPolygon',
                'coordinates': [
                    [
                        [
                            [140.86230645107744, 40.84241815568073],
                            [140.85813259170487, 40.84158550329063],
                            [140.85763230105852, 40.84130434581407],
                            [140.85867576522796, 40.839887725206204],
                            [140.86230645107744, 40.84241815568073]
                        ]
                    ],
                    [
                        [
                            [140.86405032358908, 40.842331646431276],
                            [140.86479361390965, 40.84073121276485],
                            [140.87012528939405, 40.8426884949134],
                            [140.86756665659786, 40.843218357317504],
                            [140.86405032358908, 40.842331646431276]
                        ]
                    ],
                    [
                        [
                            [140.8590331168446, 40.84672182807906],
                            [140.85863288324953, 40.843077781851804],
                            [140.86405032358908, 40.84412668223546],
                            [140.86457920311935, 40.84706783943886],
                            [140.8590331168446, 40.84672182807906]
                        ]
                    ]
                ]
            },
            'properties': {'OBJECTID': 3, 'Shape_Length': 5172.154764581936, 'Shape_Area': 475952.81528619607}
        },
        {
            'type': 'Feature',
            'id': 4,
            'geometry': {
                'type': 'MultiPolygon',
                'coordinates': [
                    [
                        [
                            [140.86835282889686, 40.847435475166115],
                            [140.8683099469185, 40.84481873092774],
                            [140.87206927870326, 40.84509987349612],
                            [140.8719263378769, 40.847630104979594],
                            [140.86835282889686, 40.847435475166115]
                        ]
                    ],
                    [
                        [
                            [140.8650366117873, 40.85230104697058],
                            [140.86463637909057, 40.84890599700013],
                            [140.8700824074157, 40.848819496219406],
                            [140.8702253473437, 40.851522579844485],
                            [140.8650366117873, 40.85230104697058]
                        ]
                    ]
                ]
            },
            'properties': {'OBJECTID': 4, 'Shape_Length': 3672.1236557090756, 'Shape_Area': 422296.0795008066}
        }
    ]
}

実行

環境

今回はsimplekmlを使用しますので、インストールしていない方は下記を参考にインストールしてください。
https://pypi.org/project/simplekml/

モジュールのインポート

import json

import simplekml

Styleの設定

Polygonごとにスタイルを変更するのが面倒なので、関数を作成して事前にスタイルを定義してしまいます。これを使用して属性ごとに色分けもできると思います。

def create_poly_style(
    normal_color: str=simplekml.Color.black,
    normal_line_width: int=1,
    highlight_color: str=simplekml.Color.yellowgreen,
    highlight_line_width: int=3,
) -> simplekml.StyleMap:
        """
        Args:
            normal_color(str): 通常時に見えるPolygonの外枠の色
            normal_line_width: 通常時に見えるPolygonの外枠の太さ
            highlight_color(str): カーソルを置いた時に見えるPolygonの外枠の色
            highlight_line_width: カーソルを置いた時に見えるPolygonの外枠の太さ
        Returns:
            (simplekml.StyleMap): simplekml.featgeom.Polygon.stylemap などに渡して色を設定する
        Example:
            >>> style_map = create_poly_style(normal_color=simplekml.Color.black,
                                              normal_line_width=1,
                                              highlight_color=simplekml.Color.yellowgreen,
                                              highlight_line_width=3)
            >>> kml = simplekml.Kml()
            >>> poly = kml.newpolygon(name='ポップアップのタイトル',
                                      outerboundaryis=outer_coors,
                                      innerboundaryis=inner_coors)
            >>> poly.stylemap = style_map
            >>> kml.save(out_fp)
        """
        
        # 通常時の色設定
        normal_style = simplekml.Style()
        normal_style.linestyle.color = normal_color
        normal_style.polystyle.fill = 0
        normal_style.linestyle.width = normal_line_width

        # カーソルを近づけた時の色設定
        highlight_style = simplekml.Style()
        highlight_style.linestyle.color = highlight_color
        highlight_style.polystyle.fill = 0
        highlight_style.linestyle.width = highlight_line_width

        return simplekml.StyleMap(normal_style, highlight_style)

属性情報の設定

def create_extended_data(properties: Dict) -> simplekml.ExtendedData:
    """
    Args:
        properties(Dict): geojson.get('features')[num].get('properties')の部分
    Retuens:
        (simplekml.ExtendedData): 
    Example:
        >>> with open('sample.geojson', mode='r') as f:
        >>>     data = json.load()
        >>>
        >>> kml = simplekml.Kml()
        >>>
        >>> for row in data.get('features'):
        >>>     --------------------------------------------
        >>>     --------------------------------------------
        >>>     extendeddata = create_extended_data(row.get('properties'))
        >>>     poly = kml.newpolygon(name='ポップアップのタイトル',
                                      outerboundaryis=outer_coors,
                                      innerboundaryis=inner_coors)
        >>>     poly.extendeddata = style_map
        >>>
        >>> kml.save(out_fp)
    
    """
    data = simplekml.ExtendedData()
    
    for key, value in properties.items():
        data.newdata(name=key, value=value)
    
    return data

geojsonからKMLに変換する関数

def polyjson_to_kml(
    out_fp: str, 
    features: Iterable[Dict], 
    style_map: simplekml.styleselector.StyleMap,
    names: Iterable[str]=None
):
        """
        Args:
            out_fp(str): 出力ファイルパス
            features(Iterable[Dict]): 
            style_map(simplekml.styleselector.StyleMap): Polygonのスタイル
            names(Iterable[str]): 
        Example:
            >>> with open('sample.geojson', mode='r') as f:
            >>>     data = json.load()
            >>>
            >>> style_map = create_poly_style(normal_color=simplekml.Color.black,
                                              normal_line_width=1,
                                              highlight_color=simplekml.Color.yellowgreen,
                                              highlight_line_width=3)
            >>>
            >>> polyjson_to_kml('output.kml', data.get('features'), style_map)
        """      
        if names is None:
             names = [i for i, n in enumerate(features)]


        kml = simplekml.Kml()

        for rows, name in zip(features, names):
            properties = rows.get('properties')
            extended = create_extended_data(properties)

            geoms = rows.get('geometry')
            geometry = geoms.get('type')
            coors = geoms.get('coordinates')
            
            if geometry == 'Polygon':
                outer = coors[0]
                inner = coors[1: ]
                poly = kml.newpolygon(name=name,
                                      outerboundaryis=outer,
                                      innerboundaryis=inner)
                poly.extendeddata = extended
                poly.stylemap = style_map

            elif geometry == 'MultiPolygon':
                multigeom = kml.newmultigeometry(name=name)
                for coor in coors:
                    outer = coor[0]
                    inner = coor[1: ]
                    poly = multigeom.newpolygon(outerboundaryis=outer,
                                                innerboundaryis=inner)
                multigeom.extendeddata = extended
                multigeom.stylemap = style_map
        
        kml.save(out_fp)

データの読み込みとkmlへの変換

in_fp = r'Datasets\sample_poly.geojson'
out_fp = r'Datasets\sample_poly.kml'

with open(in_fp, mode='r') as f:
    data = json.load(f)


style_map = create_poly_style()
polyjson_to_kml(out_fp, data.get('features'), style_map)

今回の記事を参考にすることで属性情報別に色を割り当てる事もできると思いますので、気が向いたら記事を書きたいと思っています。

Discussion