geojson の simplestyle （Mapbox GL JS 実装、popup付き）

こんにちは。
geojson の [simplestyle](https://github.com/mapbox/simplestyle-spec)（[geojson のスタイル指定](http://qiita.com/kkdd/items/46906cb1110488828d9e)）の Mapbox GL JS 実装による表示（popup付き）を作ってみました[^1]。

[^1]: Google Maps版は「[geojson の simplestyle （Google Maps 実装）](http://qiita.com/kkdd/items/07001b437d3f430006f9)」。

この [geojsonデータ](https://www.nps.gov/lib/npmap.js/3.0.18/examples/data/simplestyle.geojson) を使用した表示例です。無理を承知で作っているので見栄えは良くないですが。

<img width="453" alt="294d1ff0-d3c9-d4e5-8070-53c82782d916.jpeg" src="https://qiita-image-store.s3.amazonaws.com/0/54617/4730ac88-2108-b738-39b8-d5b5f5aadab1.jpeg">


```simplestyle.html
<html>
<head>
    <meta charset='utf-8' />
    <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
    <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.29.0/mapbox-gl.js'></script>
    <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.29.0/mapbox-gl.css' rel='stylesheet' />
    <script src='http://www.phpied.com/files/rgbcolor/rgbcolor.js'></script>
    <style>
        body { margin:0; padding:0; }
        #map { position:absolute; top:0; bottom:0; width:100%; }
    </style>
</head>
<body>
<div id='map'></div>
<script>
//mapboxgl.accessToken = '<your access token here>';
var map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox/light-v9',
    center: [-122.486052, 37.830348],
    zoom: 3
});

var idGeojson = "id-",
    regIdGeojson = new RegExp("^" + idGeojson);
var popupPos, popup = new mapboxgl.Popup({
    closeButton: false,
    closeOnClick: false
});

var geojson = {
// https://www.nps.gov/lib/npmap.js/3.0.18/examples/data/simplestyle.geojson
}

var defaultStyle={"stroke":"#aaa", "stroke-opacity":1, "fill":"#aaa", "fill-opacity":0.6, "marker-symbol":'marker', "marker-size": 'medium'};
var markerSize = {'medium': 1, 'small': 0.7, 'large': 1.6};	
var markers = ["airfield", "airport", "heliport", "rocket", "mountain", "volcano", "bakery", "bar", "beer", "cafe", "fast-food", "ice-cream", "restaurant", "college", "school", "alcohol-shop", "amusement-park", "aquarium", "art-gallery", "attraction", "bank", "bicycle", "bicycle-share", "car", "castle", "cinema", "circle", "circle-stroked", "clothing-store", "drinking-water", "embassy", "fire-station", "fuel", "grocery", "harbor", "information", "laundry", "library", "lodging", "marker", "monument", "museum", "music", "place-of-worship", "police", "post", "prison", "religious-christian", "religious-jewish", "religious-muslim", "shop", "stadium", "star", "suitcase", "swimming", "theatre", "toilet", "town-hall", "triangle", "triangle-stroked", "veterinary", "dentist", "doctor", "hospital", "pharmacy", "campsite", "cemetery", "dog-park", "garden", "golf", "park", "picnic-site", "playground", "zoo", "bus", "ferry", "entrance", "rail", "rail-light"];

function getProperty(key, properties, list) {
  var p = properties[key];
  if (p==undefined || (list!=undefined && list.indexOf(p)<0)) {p = defaultStyle[key]}
  return p;
}

// using rgbcolor.js
function rgba(color, opacity) {
  var c = new RGBColor(color);
  return "rgba("+c.r+","+c.g+","+c.b+","+opacity+")";
}

function makeLayer(id, feature) {
  var layer, properties=feature.properties;
  switch (feature.geometry.type) {
    case "Point":
      var symbol = getProperty("marker-symbol", properties, markers);
      var siz = getProperty("marker-size", properties);
      layer = {
          "type": "symbol",
          "layout": {
              "icon-image": symbol+"-15",
              "icon-size": markerSize[siz]
          }
      };
      break;
	case "LineString":
      layer = {
          "type": "line",
          "paint": {
              "line-color": getProperty("stroke", properties),
              "line-width": properties["stroke-width"],
              "line-opacity": getProperty("stroke-opacity", properties)
          }
      };
	  break;
	case "Polygon":
	  var stroke = properties["stroke"]?properties["stroke"]:properties["fill"];
	  if (stroke==undefined) {stroke=defaultStyle["stroke"]};
	  var fill = rgba(getProperty("fill", properties), getProperty("fill-opacity", properties));
	  layer = {
	      "type": "fill",
	      'paint': {
			    'fill-color': fill,
			    'fill-outline-color': stroke,
	      }   
	  };
	  break;
  }

  if (layer != undefined) {
	layer["id"] = id;
	layer["source"] = id;
  }
  return layer;
}

function addFeature(map, id, feature) {
	var l = makeLayer(id, feature);
	if (l != undefined) {
		map.addSource(id, {"type":"geojson", "data":feature});
		map.addLayer(l)
	};
}

map.on('load', function () {
	for (var i=0; i < geojson.features.length; i++) {
	    addFeature(map, idGeojson + i, geojson.features[i]);
	}
});

function coord(coord, lon) {
    var c = coord;
    c[0] += Math.round((lon-c[0])/360)*360;
    return c;
}

map.on('mousemove', function (e) {
    var features = map.queryRenderedFeatures(e.point).filter(function(x){return regIdGeojson.test(x.layer.id)});
    if (features.length > 0) {
      map.getCanvas().style.cursor = 'pointer';
      if (!popupPos) {
        popupPos = map.unproject(e.point);
        var feature = features[0];
        if (feature.geometry.type=="Point") {
          popupPos = coord(feature.geometry.coordinates, popupPos.lng);
        }
        popup.setLngLat(popupPos)
          .setHTML(feature.properties.name)
          .addTo(map);
      }
    } else {
      popup.remove();
      popupPos = undefined;
      map.getCanvas().style.cursor = '';
    }
});
</script>
</body>
</html>
```