パントレ管理部

【MapLibre】PMTiles を feature ごとに塗り分ける

page_eyecatch

 当サイトでは、トップページにある MapLibre の地図を、国ごとの関連記事数に応じて塗り分けています(記事が多いほど赤)。ここで地図には PMTiles を用いているのですが、色ごとにレイヤーを分けるのではなく、GeoJSON における feature 単位で色を変えています。以下、MapLibre で PMTiles を feature ごとに塗り分ける方法について説明します。

 まず、(ここでは国ごとの)feature に対して、properties にアルファベット3文字の国コードが含まれる GeoJSON を用意します。以下、GeoJSON の例です。

{
"type": "FeatureCollection",
"name": "countries_boundaries",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "ADM0_A3_JP": "IDN", "featurecla": "Admin-0 country", "scalerank": 0, "LABELRANK": 2, "SOVEREIGNT": "Indonesia", "SOV_A3": "IDN", "ADM0_DIF": 0, "LEVEL": 2, "TYPE": "Sovereign country", "TLC": "1", "ADMIN": "Indonesia", "ADM0_A3": "IDN", "GEOU_DIF": 0, "GEOUNIT": "Indonesia", "GU_A3": "IDN", "SU_DIF": 0, "SUBUNIT": "Indonesia", "SU_A3": "IDN", "BRK_DIFF": 0, "NAME": "Indonesia", "NAME_LONG": "Indonesia", "BRK_A3": "IDN", "BRK_NAME": "Indonesia", "BRK_GROUP": null, "ABBREV": "Indo.", "POSTAL": "INDO", "FORMAL_EN": "Republic of Indonesia", "FORMAL_FR": null, "NAME_CIAWF": "Indonesia", "NOTE_ADM0": null, "NOTE_BRK": null, "NAME_SORT": "Indonesia", "NAME_ALT": null, "MAPCOLOR7": 6, "MAPCOLOR8": 6, "MAPCOLOR9": 6, "MAPCOLOR13": 11, "POP_EST": 270625568.0, "POP_RANK": 17, "POP_YEAR": 2019, "GDP_MD": 1119190, "GDP_YEAR": 2019, "ECONOMY": "4. Emerging region: MIKT", "INCOME_GRP": "4. Lower middle income", "FIPS_10": "ID", "ISO_A2": "ID", "ISO_A2_EH": "ID", "ISO_A3": "IDN", "ISO_A3_EH": "IDN", "ISO_N3": "360", "ISO_N3_EH": "360", "UN_A3": "360", "WB_A2": "ID", "WB_A3": "IDN", "WOE_ID": 23424846, "WOE_ID_EH": 23424846, "WOE_NOTE": "Exact WOE match as country", "ADM0_ISO": "IDN", "ADM0_DIFF": null, "ADM0_TLC": "IDN", "ADM0_A3_US": "IDN", "ADM0_A3_FR": "IDN", "ADM0_A3_RU": "IDN", "ADM0_A3_ES": "IDN", "ADM0_A3_CN": "IDN", "ADM0_A3_TW": "IDN", "ADM0_A3_IN": "IDN", "ADM0_A3_NP": "IDN", "ADM0_A3_PK": "IDN", "ADM0_A3_DE": "IDN", "ADM0_A3_GB": "IDN", "ADM0_A3_BR": "IDN", "ADM0_A3_IL": "IDN", "ADM0_A3_PS": "IDN", "ADM0_A3_SA": "IDN", "ADM0_A3_EG": "IDN", "ADM0_A3_MA": "IDN", "ADM0_A3_PT": "IDN", "ADM0_A3_AR": "IDN", "ADM0_A3_KO": "IDN", "ADM0_A3_VN": "IDN", "ADM0_A3_TR": "IDN", "ADM0_A3_ID": "IDN", "ADM0_A3_PL": "IDN", "ADM0_A3_GR": "IDN", "ADM0_A3_IT": "IDN", "ADM0_A3_NL": "IDN", "ADM0_A3_SE": "IDN", "ADM0_A3_BD": "IDN", "ADM0_A3_UA": "IDN", "ADM0_A3_UN": -99, "ADM0_A3_WB": -99, "CONTINENT": "Asia", "REGION_UN": "Asia", "SUBREGION": "South-Eastern Asia", "REGION_WB": "East Asia & Pacific", "NAME_LEN": 9, "LONG_LEN": 9, "ABBREV_LEN": 5, "TINY": -99, "HOMEPART": 1, "MIN_ZOOM": 0.0, "MIN_LABEL": 1.7, "MAX_LABEL": 6.7, "LABEL_X": 101.892949, "LABEL_Y": -0.954404, "NE_ID": 1159320845, "WIKIDATAID": "Q252", "NAME_AR": "إندونيسيا", "NAME_BN": "ইন্দোনেশিয়া", "NAME_DE": "Indonesien", "NAME_EN": "Indonesia", "NAME_ES": "Indonesia", "NAME_FA": "اندونزی", "NAME_FR": "Indonésie", "NAME_EL": "Ινδονησία", "NAME_HE": "אינדונזיה", "NAME_HI": "इंडोनेशिया", "NAME_HU": "Indonézia", "NAME_ID": "Indonesia", "NAME_IT": "Indonesia", "NAME_JA": "インドネシア", "NAME_KO": "인도네시아", "NAME_NL": "Indonesië", "NAME_PL": "Indonezja", "NAME_PT": "Indonésia", "NAME_RU": "Индонезия", "NAME_SV": "Indonesien", "NAME_TR": "Endonezya", "NAME_UK": "Індонезія", "NAME_UR": "انڈونیشیا", "NAME_VI": "Indonesia", "NAME_ZH": "印度尼西亚", "NAME_ZHT": "印度尼西亞", "FCLASS_ISO": "Admin-0 country", "TLC_DIFF": null, "FCLASS_TLC": "Admin-0 country", "FCLASS_US": null, "FCLASS_FR": null, "FCLASS_RU": null, "FCLASS_ES": null, "FCLASS_CN": null, "FCLASS_TW": null, "FCLASS_IN": null, "FCLASS_NP": null, "FCLASS_PK": null, "FCLASS_DE": null, "FCLASS_GB": null, "FCLASS_BR": null, "FCLASS_IL": null, "FCLASS_PS": null, "FCLASS_SA": null, "FCLASS_EG": null, "FCLASS_MA": null, "FCLASS_PT": null, "FCLASS_AR": null, "FCLASS_JP": null, "FCLASS_KO": null, "FCLASS_VN": null, "FCLASS_TR": null, "FCLASS_ID": null, "FCLASS_PL": null, "FCLASS_GR": null, "FCLASS_IT": null, "FCLASS_NL": null, "FCLASS_SE": null, "FCLASS_BD": null, "FCLASS_UA": null }, "geometry": { "type": "MultiPolygon", "coordinates": [] } },
...
]
}


 上記の GeoJSON を PMTiles に変換します。GeoJSON の入手および PMTiles への変換については以下の記事をご参照ください。
 


 生成した PMTiles には、GeoJSON での feature ごとの properties の情報も残されています。例えば日本だけ赤色に、その他の国を灰色に塗り分けたい場合、以下のコードで実現できます。

document.addEventListener('DOMContentLoaded', function () {
    const protocol = new pmtiles.Protocol();
    maplibregl.addProtocol('pmtiles', protocol.tile);
    const PMTILES_URL = 'PMTiles の URL';
    const map =  new maplibregl.Map({
        container: 'mapcontainer',
        style: {
            version: 8,
            glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf',
            sources: {
                'countries_boundaries': {
                    type: 'vector',
                    url: `pmtiles://${PMTILES_URL}`
                }},
            layers: [
                {
                    id: 'background',
                    type: 'background',
                    paint: { 'background-color': '#dddddd' }
                },
                {
                    'id': 'countries_boundaries',
                    'type': 'fill',
                    'source': 'countries_boundaries',
                    'source-layer': 'countries_boundaries',
                    'paint': {
                        'fill-color': [
                            'match',
                            ['get', 'ADM0_A3_JP'],
                            'JPN', '#FF0000',    /*日本は赤*/
                            '#cccccc'            /*それ以外は灰色*/
                        ],
                    },
                },
                {
                    'id': 'countries_boundaries_border',
                    'type': 'line',
                    'source': 'countries_boundaries',
                    'source-layer': 'countries_boundaries',
                    'paint': {
                        'line-color': '#555555', /*国境線*/
                        'line-width': 1,
                    },
                }
            ]
        },
        center: [136, 38], 
        zoom: 0.5,
        minZoom: 0,
        maxZoom: 5,
        pitch: 0
    });
});


 以下実装例です。



 複数の国を同じ色にするのは、配列で対応できます。

document.addEventListener('DOMContentLoaded', function () {
    const protocol = new pmtiles.Protocol();
    maplibregl.addProtocol('pmtiles', protocol.tile);
    const PMTILES_URL = 'PMTiles の URL';
    const map =  new maplibregl.Map({
        container: 'mapcontainer',
        style: {
            version: 8,
            glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf',
            sources: {
                'countries_boundaries': {
                    type: 'vector',
                    url: `pmtiles://${PMTILES_URL}`
                }},
            layers: [
                {
                    id: 'background',
                    type: 'background',
                    paint: { 'background-color': '#dddddd' }
                },
                {
                    'id': 'countries_boundaries',
                    'type': 'fill',
                    'source': 'countries_boundaries',
                    'source-layer': 'countries_boundaries',
                    'paint': {
                        'fill-color': [
                            'match',
                            ['get', 'ADM0_A3_JP'],
                            ['JPN', 'CHN', 'KOR'], '#FF0000', /*日本、中国、韓国は赤*/
                            ['USA', 'CAN', 'MEX'], '#0000FF', /*アメリカ、カナダ、メキシコは青*/
                            '#cccccc'                         /*それ以外は灰色*/
                        ],
                    },
                },
                {
                    'id': 'countries_boundaries_border',
                    'type': 'line',
                    'source': 'countries_boundaries',
                    'source-layer': 'countries_boundaries',
                    'paint': {
                        'line-color': '#555555',              /*国境線*/
                        'line-width': 1,
                    },
                }
            ]
        },
        center: [136, 38], 
        zoom: 0.5,
        minZoom: 0,
        maxZoom: 5,
        pitch: 0
    });
});

 
 以下実装例です。


 
 

ちょっと宣伝

 当サイトは MapLibre を積極的に用いた海外旅ブログまとめサイトとなっています。トップページに地図がありますが、地名を押すと画像が開き、画像を押すと関連記事一覧(クリック数順)が開きます。記事数が多い国ほど赤く、地名もその国で記事が多い都市の文字が大きくなるようにしています。ぜひ覗いてみてください。
 当サイトで海外旅ブログを執筆することも可能です(無料)! また既にブログ/ YouTube をお持ちの方も、当サイトからリンクを貼ることができるようになっています。気になる方は「当サイトについて」をご覧ください。

 このページが皆様のプログラミングの一助となりますことをお祈りいたします

パントレ管理部