From 7dea1771d4fbcad20363ebab5b934f84ca85bda9 Mon Sep 17 00:00:00 2001 From: Evgeni Makarov Date: Mon, 1 Oct 2018 14:56:07 +0800 Subject: [PATCH 1/3] multiple layers initial --- .../point-viz-categorical-example.ipynb | 1475 ++++++++++++++++- .../notebooks/point-viz-types-example.ipynb | 946 ++++++++++- mapboxgl/templates.py | 7 +- mapboxgl/templates/choropleth.html | 36 +- mapboxgl/templates/circle.html | 89 +- mapboxgl/templates/circle_layers.html | 86 + mapboxgl/templates/clustered_circle.html | 130 +- .../templates/clustered_circle_layers.html | 127 ++ mapboxgl/templates/graduated_circle.html | 93 +- .../templates/graduated_circle_layers.html | 90 + mapboxgl/templates/heatmap.html | 6 +- mapboxgl/templates/image.html | 6 +- mapboxgl/templates/linestring.html | 24 +- mapboxgl/templates/map.html | 10 +- mapboxgl/templates/raster.html | 6 +- mapboxgl/templates/vector_choropleth.html | 26 +- mapboxgl/viz.py | 73 +- 17 files changed, 2826 insertions(+), 404 deletions(-) create mode 100644 mapboxgl/templates/circle_layers.html create mode 100644 mapboxgl/templates/clustered_circle_layers.html create mode 100644 mapboxgl/templates/graduated_circle_layers.html diff --git a/examples/notebooks/point-viz-categorical-example.ipynb b/examples/notebooks/point-viz-categorical-example.ipynb index 569acf3..bcd186b 100644 --- a/examples/notebooks/point-viz-categorical-example.ipynb +++ b/examples/notebooks/point-viz-categorical-example.ipynb @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": { "scrolled": false }, @@ -43,7 +43,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -53,9 +53,98 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
STATIONNAMECDEC IDUSGS IDUSACE IDCNRFC IDlatlonElevation (feet)RIVER_BASINCOUNTYOPERATORGage Type
0EL RIO (RIO MESA HIGH SCHOOL)ELHNaNNaNNaN34.2523-119.1430.0VENTURA RVENTURAVentura Countytemp
1GOLDEN GATEGGTNaNNaNNaN37.8100-122.4740.0SF BAYSAN FRANCISCONational Weather Servicetemp
\n", + "
" + ], + "text/plain": [ + " STATIONNAME CDEC ID USGS ID USACE ID CNRFC ID lat \\\n", + "0 EL RIO (RIO MESA HIGH SCHOOL) ELH NaN NaN NaN 34.2523 \n", + "1 GOLDEN GATE GGT NaN NaN NaN 37.8100 \n", + "\n", + " lon Elevation (feet) RIVER_BASIN COUNTY \\\n", + "0 -119.143 0.0 VENTURA R VENTURA \n", + "1 -122.474 0.0 SF BAY SAN FRANCISCO \n", + "\n", + " OPERATOR Gage Type \n", + "0 Ventura County temp \n", + "1 National Weather Service temp " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Load data from sample csv\n", "data_url = 'https://raw.githubusercontent.com/mapbox/mapboxgl-jupyter/master/examples/data/cdec.csv'\n", @@ -72,9 +161,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "{'feature_count': 2353, 'filename': '../data/cdec.geojson', 'type': 'file'}" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Create geojson data object\n", "df_to_geojson(df.fillna(''), \n", @@ -85,9 +185,435 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 5, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "54497424-d26c-43f8-8902-a99d5306aa22\n" + ] + } + ], "source": [ "# Assign color stops\n", "category_color_stops = [['reservoir', 'rgb(211,47,47)'], \n", @@ -110,7 +636,8 @@ " zoom=4.5)\n", "\n", "# Render map\n", - "viz.show() " + "viz.show()\n", + "print(viz.uid)" ] }, { @@ -122,9 +649,424 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Numeric color stops from ColorBrewer\n", "sample_color_stops = [[0.0, 'rgb(255,255,204)'],\n", @@ -166,6 +1108,506 @@ "## Combination of match-type and interpolate-type color and radius assignment" ] }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "len(self.visualisations) 2\n", + "2\n" + ] + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "viz_c = VizCollection([viz, viz2])\n", + "viz_c.show()" + ] + }, { "cell_type": "code", "execution_count": null, @@ -241,6 +1683,13 @@ "# Render map\n", "viz4.show()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -264,7 +1713,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.1" + "version": "3.6.2" } }, "nbformat": 4, diff --git a/examples/notebooks/point-viz-types-example.ipynb b/examples/notebooks/point-viz-types-example.ipynb index 4572a56..82a3d36 100644 --- a/examples/notebooks/point-viz-types-example.ipynb +++ b/examples/notebooks/point-viz-types-example.ipynb @@ -20,7 +20,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -32,9 +32,92 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Avg Total PaymentsAvg Covered ChargesTotal DischargesAvg Medicare Paymentsadmin1_idProvider Idadmin2_idlonlatdate
08749.02535247.02858.7507678.214USA10110001USA201069-85.36331.2161/1/14 12:00 AM
16812.13116451.09228.9595793.631USA10110005USA201119-88.14332.4531/2/14 12:00 AM
\n", + "
" + ], + "text/plain": [ + " Avg Total Payments Avg Covered Charges Total Discharges \\\n", + "0 8749.025 35247.028 58.750 \n", + "1 6812.131 16451.092 28.959 \n", + "\n", + " Avg Medicare Payments admin1_id Provider Id admin2_id lon lat \\\n", + "0 7678.214 USA101 10001 USA201069 -85.363 31.216 \n", + "1 5793.631 USA101 10005 USA201119 -88.143 32.453 \n", + "\n", + " date \n", + "0 1/1/14 12:00 AM \n", + "1 1/2/14 12:00 AM " + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Load data from sample csv\n", "data_url = 'https://raw.githubusercontent.com/mapbox/mapboxgl-jupyter/master/examples/data/points.csv'\n", @@ -54,7 +137,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -71,9 +154,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "{'feature_count': 3173,\n", + " 'filename': '../data/healthcare_points.geojson',\n", + " 'type': 'file'}" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Create a geojson file export from the current dataframe\n", "df_to_geojson(df, \n", @@ -86,9 +182,422 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Just show a map of the data\n", "viz = CircleViz('../data/healthcare_points.geojson', \n", @@ -101,11 +610,426 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": { "scrolled": false }, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Generate data breaks using numpy quantiles and color stops from colorBrewer\n", "measure = 'Avg Medicare Payments'\n", @@ -364,7 +1288,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.1" + "version": "3.6.2" } }, "nbformat": 4, diff --git a/mapboxgl/templates.py b/mapboxgl/templates.py index 6a6f034..f5b1f86 100644 --- a/mapboxgl/templates.py +++ b/mapboxgl/templates.py @@ -7,6 +7,9 @@ ) -def format(viz, **kwargs): - template = env.get_template('{}.html'.format(viz)) +def format(viz, from_string=False, template_str=None, **kwargs): + if from_string: + template = env.from_string(template_str) + else: + template = env.get_template('{}.html'.format(viz)) return template.render(viz=viz, **kwargs) diff --git a/mapboxgl/templates/choropleth.html b/mapboxgl/templates/choropleth.html index 59fb005..00e2f23 100644 --- a/mapboxgl/templates/choropleth.html +++ b/mapboxgl/templates/choropleth.html @@ -8,7 +8,7 @@ {% endblock extra_css %} {% block legend %} - + {% if showLegend %} {% if extrudeChoropleth %} {% if colorStops and colorProperty and heightProperty %} @@ -28,11 +28,11 @@ {% block map %} map.on('style.load', function() { - + {% block choropleth %} // Add geojson data source - map.addSource("data", { + map.addSource("data" + "_" + "{{uid}}", { "type": "geojson", "data": {{ geojson_data }}, "buffer": 1, @@ -41,8 +41,8 @@ // Add data layer map.addLayer({ - "id": "choropleth-fill", - "source": "data", + "id": "choropleth-fill" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", "type": "fill", "paint": { "fill-color": generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}"), @@ -52,8 +52,8 @@ // Add border layer map.addLayer({ - "id": "choropleth-line", - "source": "data", + "id": "choropleth-line" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", "type": "line", "layout": { "line-join": "round", @@ -71,8 +71,8 @@ // Add label layer map.addLayer({ - "id": "choropleth-label", - "source": "data", + "id": "choropleth-label" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", "type": "symbol", "layout": { {% if labelProperty %} @@ -91,9 +91,9 @@ {% if extrudeChoropleth %} map.addLayer({ - id: "choropleth-extrusion", - type: "fill-extrusion", - source: "data", + id: "choropleth-extrusion" + "_" + "{{uid}}", + type: "fill-extrusion", + source: "data" + "_" + "{{uid}}", paint: { "fill-extrusion-opacity": {{ opacity }}, "fill-extrusion-color": generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}"), @@ -111,13 +111,13 @@ closeButton: false, closeOnClick: false }); - + {% block choropleth_popup %} // Show the popup on mouseover - map.on('mousemove', 'choropleth-fill', function(e) { + map.on('mousemove', 'choropleth-fill' + "_" + "{{uid}}", function(e) { map.getCanvas().style.cursor = 'pointer'; - + let f = e.features[0]; let popup_html = '
'; @@ -133,13 +133,13 @@ {% endblock choropleth_popup %} - map.on('mouseleave', 'choropleth-fill', function() { + map.on('mouseleave', 'choropleth-fill' + "_" + "{{uid}}", function() { map.getCanvas().style.cursor = ''; popup.remove(); }); - + // Fly to on click - map.on('click', 'choropleth-fill', function(e) { + map.on('click', 'choropleth-fill' + "_" + "{{uid}}", function(e) { map.flyTo({ center: e.lngLat, zoom: map.getZoom() + 1 diff --git a/mapboxgl/templates/circle.html b/mapboxgl/templates/circle.html index c9be63f..c51da22 100644 --- a/mapboxgl/templates/circle.html +++ b/mapboxgl/templates/circle.html @@ -14,96 +14,11 @@ calcColorLegend({{ colorStops }} , "{{ colorProperty }}"); {% endif %} {% endif %} - + {% endblock legend %} {% block map %} - map.on('style.load', function() { - - map.addSource("data", { - "type": "geojson", - "data": {{ geojson_data }}, - "buffer": 1, - "maxzoom": 14 - }); - - map.addLayer({ - "id": "label", - "source": "data", - "type": "symbol", - "maxzoom": {{ maxzoom }}, - "minzoom": {{ minzoom }}, - "layout": { - {% if labelProperty %} - "text-field": "{{ labelProperty }}", - {% endif %} - "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), - "text-offset": [0,-1] - }, - "paint": { - "text-halo-color": "{{ labelHaloColor }}", - "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), - "text-color": "{{ labelColor }}" - } - }, "{{belowLayer}}" ) - - map.addLayer({ - "id": "circle", - "source": "data", - "type": "circle", - "maxzoom": {{ maxzoom }}, - "minzoom": {{ minzoom }}, - "paint": { - {% if colorProperty %} - "circle-color": generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}" ), - {% else %} - "circle-color": "{{ defaultColor }}", - {% endif %} - "circle-radius" : generatePropertyExpression('interpolate', 'zoom', [[0,{{ radius }}], [22,10 * {{ radius }}]]), - "circle-stroke-color": "{{ strokeColor }}", - "circle-stroke-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ strokeWidth }}], [18,5* {{ strokeWidth }}]]), - "circle-opacity" : {{ opacity }}, - "circle-stroke-opacity" : {{ opacity }} - } - }, "label"); - - // Create a popup - var popup = new mapboxgl.Popup({ - closeButton: false, - closeOnClick: false - }); - - // Show the popup on mouseover - map.on('mousemove', 'circle', function(e) { - map.getCanvas().style.cursor = 'pointer'; - - let f = e.features[0]; - let popup_html = '
  • Location: ' + f.geometry.coordinates[0].toPrecision(6) + - ', ' + f.geometry.coordinates[1].toPrecision(6) + '
  • '; - - for (key in f.properties) { - popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' - } - - popup_html += '
    ' - popup.setLngLat(e.features[0].geometry.coordinates) - .setHTML(popup_html) - .addTo(map); - }); - - map.on('mouseleave', 'circle', function() { - map.getCanvas().style.cursor = ''; - popup.remove(); - }); - - // Fly to on click - map.on('click', 'circle', function(e) { - map.easeTo({ - center: e.features[0].geometry.coordinates, - zoom: map.getZoom() + 1 - }); - }); - }); + {% include 'circle_layers.html' %} {% endblock map %} diff --git a/mapboxgl/templates/circle_layers.html b/mapboxgl/templates/circle_layers.html new file mode 100644 index 0000000..c09ecc4 --- /dev/null +++ b/mapboxgl/templates/circle_layers.html @@ -0,0 +1,86 @@ +map.on('style.load', function() { + + map.addSource("data" + "_" + "{{uid}}", { + "type": "geojson", + "data": {{ geojson_data }}, + "buffer": 1, + "maxzoom": 14 + }); + + map.addLayer({ + "id": "label" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", + "type": "symbol", + "maxzoom": {{ maxzoom }}, + "minzoom": {{ minzoom }}, + "layout": { + {% if labelProperty %} + "text-field": "{{ labelProperty }}", + {% endif %} + "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), + "text-offset": [0,-1] + }, + "paint": { + "text-halo-color": "{{ labelHaloColor }}", + "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), + "text-color": "{{ labelColor }}" + } + }, "{{belowLayer}}" ) + + map.addLayer({ + "id": "circle" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", + "type": "circle", + "maxzoom": {{ maxzoom }}, + "minzoom": {{ minzoom }}, + "paint": { + {% if colorProperty %} + "circle-color": generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}" ), + {% else %} + "circle-color": "{{ defaultColor }}", + {% endif %} + "circle-radius" : generatePropertyExpression('interpolate', 'zoom', [[0,{{ radius }}], [22,10 * {{ radius }}]]), + "circle-stroke-color": "{{ strokeColor }}", + "circle-stroke-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ strokeWidth }}], [18,5* {{ strokeWidth }}]]), + "circle-opacity" : {{ opacity }}, + "circle-stroke-opacity" : {{ opacity }} + } + }, "label" + "_" + "{{uid}}"); + + // Create a popup + var popup = new mapboxgl.Popup({ + closeButton: false, + closeOnClick: false + }); + + // Show the popup on mouseover + map.on('mousemove', 'circle' + "_" + "{{uid}}", function(e) { + map.getCanvas().style.cursor = 'pointer'; + + let f = e.features[0]; + let popup_html = '
  • Location: ' + f.geometry.coordinates[0].toPrecision(6) + + ', ' + f.geometry.coordinates[1].toPrecision(6) + '
  • '; + + for (key in f.properties) { + popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' + } + + popup_html += '
    ' + popup.setLngLat(e.features[0].geometry.coordinates) + .setHTML(popup_html) + .addTo(map); + }); + + map.on('mouseleave', 'circle' + "_" + "{{uid}}", function() { + map.getCanvas().style.cursor = ''; + popup.remove(); + }); + + // Fly to on click + map.on('click', 'circle' + "_" + "{{uid}}", function(e) { + map.easeTo({ + center: e.features[0].geometry.coordinates, + zoom: map.getZoom() + 1 + }); + }); +}); diff --git a/mapboxgl/templates/clustered_circle.html b/mapboxgl/templates/clustered_circle.html index 5a6717e..b712936 100644 --- a/mapboxgl/templates/clustered_circle.html +++ b/mapboxgl/templates/clustered_circle.html @@ -8,7 +8,7 @@ {% endblock extra_css %} {% block legend %} - + {% if showLegend %} calcColorLegend({{ colorStops }}, "Point Density"); {% endif %} @@ -17,132 +17,6 @@ {% block map %} - map.on('style.load', function() { - - map.addSource("data", { - "type": "geojson", - "data": {{ geojson_data }}, //data from dataframe output to geojson - "buffer": 0, - "maxzoom": {{ clusterMaxZoom }} + 1, - "cluster": true, - "clusterMaxZoom": {{ clusterMaxZoom }}, - "clusterRadius": {{ clusterRadius }} - }); - - map.addLayer({ - "id": "label", - "source": "data", - "type": "symbol", - "maxzoom": {{ maxzoom }}, - "minzoom": {{ minzoom }}, - "layout": { - "text-field": "{point_count_abbreviated}", - "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), - }, - "paint": { - "text-halo-color": "{{ labelHaloColor }}", - "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), - "text-color": "{{ labelColor }}" - } - }, "{{belowLayer}}" ) - - map.addLayer({ - "id": "circle-cluster", - "source": "data", - "type": "circle", - "maxzoom": {{ maxzoom }}, - "minzoom": {{ minzoom }}, - "filter": ["has", "point_count"], - "paint": { - "circle-color": generateInterpolateExpression( "point_count", {{ colorStops }} ), - "circle-radius" : generateInterpolateExpression( "point_count", {{ radiusStops }} ), - "circle-stroke-color": "{{ strokeColor }}", - "circle-stroke-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ strokeWidth }}], [18,5* {{ strokeWidth }}]]), - "circle-opacity" : {{ opacity }}, - "circle-stroke-opacity" : {{ opacity }} - } - }, "label"); - - map.addLayer({ - "id": "circle-unclustered", - "source": "data", - "type": "circle", - "maxzoom": {{ maxzoom }}, - "minzoom": {{ minzoom }}, - "filter": ["!has", "point_count"], - "paint": { - "circle-color": "{{ colorDefault }}", - "circle-radius" : generateInterpolateExpression( 'zoom', [[0, {{ radiusDefault }} ], [22,10 * {{ radiusDefault }}]]), - "circle-stroke-color": "{{ strokeColor }}", - "circle-stroke-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ strokeWidth }}], [18,5* {{ strokeWidth }}]]), - "circle-opacity" : {{ opacity }}, - "circle-stroke-opacity" : {{ opacity }} - } - }, "circle-cluster"); - - // Create a popup - var popup = new mapboxgl.Popup({ - closeButton: false, - closeOnClick: false - }); - - // Show the popup on mouseover - map.on('mousemove', 'circle-unclustered', function(e) { - map.getCanvas().style.cursor = 'pointer'; - let f = e.features[0]; - let popup_html = '
  • Location: ' + f.geometry.coordinates[0].toPrecision(6) + - ', ' + f.geometry.coordinates[1].toPrecision(6) + '
  • '; - - for (key in f.properties) { - popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' - } - - popup_html += '
    ' - - popup.setLngLat(e.features[0].geometry.coordinates) - .setHTML(popup_html) - .addTo(map); - }); - - map.on('mousemove', 'circle-cluster', function(e) { - map.getCanvas().style.cursor = 'pointer'; - let f = e.features[0]; - let popup_html = '
  • Location: ' + f.geometry.coordinates[0].toPrecision(6) + - ', ' + f.geometry.coordinates[1].toPrecision(6) + '
  • '; - - popup_html += '
  • Point Count:: ' + f.properties.point_count + '
  • ' - - popup_html += '
    ' - - popup.setLngLat(e.features[0].geometry.coordinates) - .setHTML(popup_html) - .addTo(map); - }); - - map.on('mouseleave', 'circle-unclustered', function() { - map.getCanvas().style.cursor = ''; - popup.remove(); - }); - - map.on('mouseleave', 'circle-cluster', function() { - map.getCanvas().style.cursor = ''; - popup.remove(); - }); - - - map.on('click', 'circle-unclustered', function(e) { - map.easeTo({ - center: e.features[0].geometry.coordinates, - zoom: map.getZoom() + 1 - }); - }); - - map.on('click', 'circle-cluster', function(e) { - map.easeTo({ - center: e.features[0].geometry.coordinates, - zoom: map.getZoom() + 1 - }); - }); - }); + {% include 'clustered_circle_layers.html' %} {% endblock map %} diff --git a/mapboxgl/templates/clustered_circle_layers.html b/mapboxgl/templates/clustered_circle_layers.html new file mode 100644 index 0000000..6e00ba4 --- /dev/null +++ b/mapboxgl/templates/clustered_circle_layers.html @@ -0,0 +1,127 @@ +map.on('style.load', function() { + + map.addSource("data" + "_" + "{{uid}}", { + "type": "geojson", + "data": {{ geojson_data }}, //data from dataframe output to geojson + "buffer": 0, + "maxzoom": {{ clusterMaxZoom }} + 1, + "cluster": true, + "clusterMaxZoom": {{ clusterMaxZoom }}, + "clusterRadius": {{ clusterRadius }} + }); + + map.addLayer({ + "id": "label" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", + "type": "symbol", + "maxzoom": {{ maxzoom }}, + "minzoom": {{ minzoom }}, + "layout": { + "text-field": "{point_count_abbreviated}", + "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), + }, + "paint": { + "text-halo-color": "{{ labelHaloColor }}", + "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), + "text-color": "{{ labelColor }}" + } + }, "{{belowLayer}}" ) + + map.addLayer({ + "id": "circle-cluster" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", + "type": "circle", + "maxzoom": {{ maxzoom }}, + "minzoom": {{ minzoom }}, + "filter": ["has", "point_count"], + "paint": { + "circle-color": generateInterpolateExpression( "point_count", {{ colorStops }} ), + "circle-radius" : generateInterpolateExpression( "point_count", {{ radiusStops }} ), + "circle-stroke-color": "{{ strokeColor }}", + "circle-stroke-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ strokeWidth }}], [18,5* {{ strokeWidth }}]]), + "circle-opacity" : {{ opacity }}, + "circle-stroke-opacity" : {{ opacity }} + } + }, "label" + "_" + "{{uid}}"); + + map.addLayer({ + "id": "circle-unclustered" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", + "type": "circle", + "maxzoom": {{ maxzoom }}, + "minzoom": {{ minzoom }}, + "filter": ["!has", "point_count"], + "paint": { + "circle-color": "{{ colorDefault }}", + "circle-radius" : generateInterpolateExpression( 'zoom', [[0, {{ radiusDefault }} ], [22,10 * {{ radiusDefault }}]]), + "circle-stroke-color": "{{ strokeColor }}", + "circle-stroke-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ strokeWidth }}], [18,5* {{ strokeWidth }}]]), + "circle-opacity" : {{ opacity }}, + "circle-stroke-opacity" : {{ opacity }} + } + }, "circle-cluster" + "_" + "{{uid}}"); + + // Create a popup + var popup = new mapboxgl.Popup({ + closeButton: false, + closeOnClick: false + }); + + // Show the popup on mouseover + map.on('mousemove', 'circle-unclustered' + "_" + "{{uid}}", function(e) { + map.getCanvas().style.cursor = 'pointer'; + let f = e.features[0]; + let popup_html = '
  • Location: ' + f.geometry.coordinates[0].toPrecision(6) + + ', ' + f.geometry.coordinates[1].toPrecision(6) + '
  • '; + + for (key in f.properties) { + popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' + } + + popup_html += '
    ' + + popup.setLngLat(e.features[0].geometry.coordinates) + .setHTML(popup_html) + .addTo(map); + }); + + map.on('mousemove', 'circle-cluster' + "_" + "{{uid}}", function(e) { + map.getCanvas().style.cursor = 'pointer'; + let f = e.features[0]; + let popup_html = '
  • Location: ' + f.geometry.coordinates[0].toPrecision(6) + + ', ' + f.geometry.coordinates[1].toPrecision(6) + '
  • '; + + popup_html += '
  • Point Count:: ' + f.properties.point_count + '
  • ' + + popup_html += '
    ' + + popup.setLngLat(e.features[0].geometry.coordinates) + .setHTML(popup_html) + .addTo(map); + }); + + map.on('mouseleave', 'circle-unclustered' + "_" + "{{uid}}", function() { + map.getCanvas().style.cursor = ''; + popup.remove(); + }); + + map.on('mouseleave', 'circle-cluster' + "_" + "{{uid}}", function() { + map.getCanvas().style.cursor = ''; + popup.remove(); + }); + + + map.on('click', 'circle-unclustered' + "_" + "{{uid}}", function(e) { + map.easeTo({ + center: e.features[0].geometry.coordinates, + zoom: map.getZoom() + 1 + }); + }); + + map.on('click', 'circle-cluster' + "_" + "{{uid}}", function(e) { + map.easeTo({ + center: e.features[0].geometry.coordinates, + zoom: map.getZoom() + 1 + }); + }); +}); diff --git a/mapboxgl/templates/graduated_circle.html b/mapboxgl/templates/graduated_circle.html index b95cb40..282df00 100644 --- a/mapboxgl/templates/graduated_circle.html +++ b/mapboxgl/templates/graduated_circle.html @@ -8,7 +8,7 @@ {% endblock extra_css %} {% block legend %} - + {% if showLegend %} {% if colorStops and colorProperty and radiusProperty %} calcColorLegend({{ colorStops }}, "{{ colorProperty }} vs. {{ radiusProperty }}"); @@ -19,95 +19,6 @@ {% block map %} - map.on('style.load', function() { - - map.addSource("data", { - "type": "geojson", - "data": {{ geojson_data }}, //data from dataframe output to geojson - "buffer": 0, - "maxzoom": 14 - }); - - map.addLayer({ - "id": "label", - "source": "data", - "type": "symbol", - "maxzoom": {{ maxzoom }}, - "minzoom": {{ minzoom }}, - "layout": { - {% if labelProperty %} - "text-field": "{{ labelProperty }}", - {% endif %} - "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), - "text-offset": [0,-1] - }, - "paint": { - "text-halo-color": "{{ labelHaloColor }}", - "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), - "text-color": "{{ labelColor }}" - } - }, "{{belowLayer}}" ) - - map.addLayer({ - "id": "circle", - "source": "data", - "type": "circle", - "maxzoom": {{ maxzoom }}, - "minzoom": {{ minzoom }}, - "paint": { - {% if colorProperty %} - "circle-color": generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}" ), - {% else %} - "circle-color": "{{ defaultColor }}", - {% endif %} - {% if radiusProperty %} - "circle-radius" : generatePropertyExpression("{{ radiusType }}", "{{ radiusProperty }}", {{ radiusStops }}, {{ defaultRadius }} ), - {% else %} - "circle-radius": {{ defaultRadius }}, - {% endif %} - "circle-stroke-color": "{{ strokeColor }}", - "circle-stroke-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ strokeWidth }}], [18,5* {{ strokeWidth }}]]), - "circle-opacity" : {{ opacity }}, - "circle-stroke-opacity" : {{ opacity }} - } - }, "label"); - - // Create a popup - var popup = new mapboxgl.Popup({ - closeButton: false, - closeOnClick: false - }); - - // Show the popup on mouseover - map.on('mousemove', 'circle', function(e) { - map.getCanvas().style.cursor = 'pointer'; - let f = e.features[0]; - let popup_html = '
  • Location: ' + f.geometry.coordinates[0].toPrecision(6) + - ', ' + f.geometry.coordinates[1].toPrecision(6) + '
  • '; - - for (key in f.properties) { - popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' - } - - popup_html += '
    ' - - popup.setLngLat(e.features[0].geometry.coordinates) - .setHTML(popup_html) - .addTo(map); - }); + {% include 'graduated_circle_layers.html' %} - map.on('mouseleave', 'circle', function() { - map.getCanvas().style.cursor = ''; - popup.remove(); - }); - - // Fly to on click - map.on('click', 'circle', function(e) { - map.easeTo({ - center: e.features[0].geometry.coordinates, - zoom: map.getZoom() + 1 - }); - }); - }); - {% endblock map %} diff --git a/mapboxgl/templates/graduated_circle_layers.html b/mapboxgl/templates/graduated_circle_layers.html new file mode 100644 index 0000000..e5130ff --- /dev/null +++ b/mapboxgl/templates/graduated_circle_layers.html @@ -0,0 +1,90 @@ +map.on('style.load', function() { + + map.addSource("data" + "_" + "{{uid}}", { + "type": "geojson", + "data": {{ geojson_data }}, //data from dataframe output to geojson + "buffer": 0, + "maxzoom": 14 + }); + + map.addLayer({ + "id": "label" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", + "type": "symbol", + "maxzoom": {{ maxzoom }}, + "minzoom": {{ minzoom }}, + "layout": { + {% if labelProperty %} + "text-field": "{{ labelProperty }}", + {% endif %} + "text-size" : generateInterpolateExpression('zoom', [[0, {{ labelSize }}],[22, 3* {{ labelSize }}]] ), + "text-offset": [0,-1] + }, + "paint": { + "text-halo-color": "{{ labelHaloColor }}", + "text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]), + "text-color": "{{ labelColor }}" + } + }, "{{belowLayer}}" ) + + map.addLayer({ + "id": "circle" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", + "type": "circle", + "maxzoom": {{ maxzoom }}, + "minzoom": {{ minzoom }}, + "paint": { + {% if colorProperty %} + "circle-color": generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}" ), + {% else %} + "circle-color": "{{ defaultColor }}", + {% endif %} + {% if radiusProperty %} + "circle-radius" : generatePropertyExpression("{{ radiusType }}", "{{ radiusProperty }}", {{ radiusStops }}, {{ defaultRadius }} ), + {% else %} + "circle-radius": {{ defaultRadius }}, + {% endif %} + "circle-stroke-color": "{{ strokeColor }}", + "circle-stroke-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ strokeWidth }}], [18,5* {{ strokeWidth }}]]), + "circle-opacity" : {{ opacity }}, + "circle-stroke-opacity" : {{ opacity }} + } + }, "label" + "_" + "{{uid}}"); + + // Create a popup + var popup = new mapboxgl.Popup({ + closeButton: false, + closeOnClick: false + }); + + // Show the popup on mouseover + map.on('mousemove', 'circle' + "_" + "{{uid}}", function(e) { + map.getCanvas().style.cursor = 'pointer'; + let f = e.features[0]; + let popup_html = '
  • Location: ' + f.geometry.coordinates[0].toPrecision(6) + + ', ' + f.geometry.coordinates[1].toPrecision(6) + '
  • '; + + for (key in f.properties) { + popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' + } + + popup_html += '
    ' + + popup.setLngLat(e.features[0].geometry.coordinates) + .setHTML(popup_html) + .addTo(map); + }); + + map.on('mouseleave', 'circle' + "_" + "{{uid}}", function() { + map.getCanvas().style.cursor = ''; + popup.remove(); + }); + + // Fly to on click + map.on('click', 'circle' + "_" + "{{uid}}", function(e) { + map.easeTo({ + center: e.features[0].geometry.coordinates, + zoom: map.getZoom() + 1 + }); + }); +}); diff --git a/mapboxgl/templates/heatmap.html b/mapboxgl/templates/heatmap.html index 2d3dd90..c20ead4 100644 --- a/mapboxgl/templates/heatmap.html +++ b/mapboxgl/templates/heatmap.html @@ -6,7 +6,7 @@ map.on('style.load', function() { - map.addSource("data", { + map.addSource("data" + "_" + "{{uid}}", { "type": "geojson", "data": {{ geojson_data }}, //data from dataframe output to geojson "buffer": 0, @@ -14,8 +14,8 @@ }); map.addLayer({ - "id": "circle", - "source": "data", + "id": "circle" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", "type": "heatmap", "maxzoom": {{ maxzoom }}, "minzoom": {{ minzoom }}, diff --git a/mapboxgl/templates/image.html b/mapboxgl/templates/image.html index 194f356..22a2cb7 100644 --- a/mapboxgl/templates/image.html +++ b/mapboxgl/templates/image.html @@ -6,16 +6,16 @@ map.on('style.load', function() { - map.addSource("image", { + map.addSource("image" + "_" + "{{uid}}", { "type": "image", "url": "{{ image }}", // url or base64 encoded image "coordinates": {{ coordinates }} }); map.addLayer({ - "id": 'image', + "id": 'image' + "_" + "{{uid}}", "type": "raster", - "source": "image" + "source": "image" + "_" + "{{uid}}" }, "{{belowLayer}}"); }); diff --git a/mapboxgl/templates/linestring.html b/mapboxgl/templates/linestring.html index 760b273..18b4c1a 100644 --- a/mapboxgl/templates/linestring.html +++ b/mapboxgl/templates/linestring.html @@ -19,11 +19,11 @@ {% block map %} map.on('style.load', function() { - + {% block linestring %} // Add geojson data source - map.addSource("data", { + map.addSource("data" + "_" + "{{uid}}", { "type": "geojson", "data": {{ geojson_data }}, "buffer": 1, @@ -32,8 +32,8 @@ // Add data layer map.addLayer({ - "id": "linestring", - "source": "data", + "id": "linestring" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", "type": "line", "layout": { "line-join": "round", @@ -59,8 +59,8 @@ // Add label layer map.addLayer({ - "id": "linestring-label", - "source": "data", + "id": "linestring-label" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", "type": "symbol", "layout": { {% if labelProperty %} @@ -83,13 +83,13 @@ closeButton: false, closeOnClick: false }); - + {% block linestring_popup %} // Show the popup on mouseover - map.on('mousemove', 'linestring', function(e) { + map.on('mousemove', 'linestring' + "_" + "{{uid}}", function(e) { map.getCanvas().style.cursor = 'pointer'; - + let f = e.features[0]; let popup_html = '
    '; @@ -105,13 +105,13 @@ {% endblock linestring_popup %} - map.on('mouseleave', 'linestring', function() { + map.on('mouseleave', 'linestring' + "_" + "{{uid}}", function() { map.getCanvas().style.cursor = ''; popup.remove(); }); - + // Fly to on click - map.on('click', 'linestring', function(e) { + map.on('click', 'linestring' + "_" + "{{uid}}", function(e) { map.flyTo({ center: e.lngLat, zoom: map.getZoom() + 1 diff --git a/mapboxgl/templates/map.html b/mapboxgl/templates/map.html index 5f5c340..c927e4f 100644 --- a/mapboxgl/templates/map.html +++ b/mapboxgl/templates/map.html @@ -5,19 +5,19 @@ {% block map %} map.on('style.load', function() { - + // Add data source - map.addSource("data", { + map.addSource("data" + "_" + "{{uid}}", { "type": "geojson", "data": {{ geojson_data }}, "buffer": 1, "maxzoom": 14 }); - + // Add data layer map.addLayer({ - "id": "circle", - "source": "data", + "id": "circle" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", "type": "circle", "maxzoom": {{ maxzoom }}, "minzoom": {{ minzoom }}, diff --git a/mapboxgl/templates/raster.html b/mapboxgl/templates/raster.html index 1561ed1..99a8d9a 100644 --- a/mapboxgl/templates/raster.html +++ b/mapboxgl/templates/raster.html @@ -17,12 +17,12 @@ } if ({{ tiles_bounds }} !== undefined) params.bounds = {{ tiles_bounds }}; - map.addSource("raster-tiles", params); + map.addSource("raster-tiles" + "_" + "{{uid}}", params); map.addLayer({ - "id": "raster-tiles", + "id": "raster-tiles" + "_" + "{{uid}}", "type": "raster", - "source": "raster-tiles" + "source": "raster-tiles" + "_" + "{{uid}}" }, "{{belowLayer}}" ); }); diff --git a/mapboxgl/templates/vector_choropleth.html b/mapboxgl/templates/vector_choropleth.html index 139b913..9eb60e4 100644 --- a/mapboxgl/templates/vector_choropleth.html +++ b/mapboxgl/templates/vector_choropleth.html @@ -11,10 +11,10 @@ // Create filter for layers from join data let layerFilter = ['in', "{{ vectorJoinDataProperty }}"] - + joinData.forEach(function(row, index) { popUpKeys[row["{{ dataJoinProperty }}"]] = row["{{ colorProperty }}"]; - + {% if extrudeChoropleth %} {% if colorProperty != heightProperty %} heightPopUpKeys[row["{{ dataJoinProperty }}"]] = row["{{ heightProperty }}"]; @@ -41,10 +41,10 @@ "paint": { "fill-color": { "type": "categorical", - "property": "{{ vectorJoinDataProperty }}", - "stops": {{ vectorColorStops }}, + "property": "{{ vectorJoinDataProperty }}", + "stops": {{ vectorColorStops }}, "default": "{{ defaultColor }}" - }, + }, "fill-opacity": {{ opacity }} }, filter: layerFilter @@ -93,24 +93,24 @@ // Optional extrusion layer {% if extrudeChoropleth %} - + map.addLayer({ id: "choropleth-extrusion", - type: "fill-extrusion", + type: "fill-extrusion", "source": "vector-data", "source-layer": "{{ vectorLayer }}", paint: { "fill-extrusion-opacity": {{ opacity }}, "fill-extrusion-color": { "type": "categorical", - "property": "{{ vectorJoinDataProperty }}", - "stops": {{ vectorColorStops }}, + "property": "{{ vectorJoinDataProperty }}", + "stops": {{ vectorColorStops }}, "default": "{{ defaultColor }}" }, "fill-extrusion-height": { "type": "categorical", - "property": "{{ vectorJoinDataProperty }}", - "stops": {{ vectorHeightStops }}, + "property": "{{ vectorJoinDataProperty }}", + "stops": {{ vectorHeightStops }}, "default": {{ defaultHeight }} } }, @@ -126,7 +126,7 @@ // Show the popup on mouseover map.on('mousemove', 'choropleth-fill', function(e) { map.getCanvas().style.cursor = 'pointer'; - + let f = e.features[0]; let popup_html = '
    '; @@ -136,7 +136,7 @@ // Add property from joined data to vector's feature popup popup_html += '
  • ' + "{{ colorProperty }}".toUpperCase() + ': ' + popUpKeys[f.properties["{{ vectorJoinDataProperty }}"]] + '
  • ' - + {% if extrudeChoropleth %} {% if colorProperty != heightProperty %} popup_html += '
  • ' + "{{ heightProperty }}".toUpperCase() + ': ' + heightPopUpKeys[f.properties["{{ vectorJoinDataProperty }}"]] + '
  • ' diff --git a/mapboxgl/viz.py b/mapboxgl/viz.py index 87d6418..ece6f44 100644 --- a/mapboxgl/viz.py +++ b/mapboxgl/viz.py @@ -1,6 +1,7 @@ import codecs import json import os +import uuid from IPython.core.display import HTML, display @@ -83,6 +84,7 @@ def __init__(self, raise TokenError('Mapbox access token must be public (pk), not secret (sk). ' \ 'Please sign up at https://www.mapbox.com/signup/ to get a public token. ' \ 'If you already have an account, you can retreive your token at https://www.mapbox.com/account/.') + self.uid = str(uuid.uuid4()) self.access_token = access_token self.template = 'map' self.data = data @@ -137,8 +139,8 @@ def show(self, **kwargs): def add_unique_template_variables(self, options): pass - def create_html(self, filename=None): - """Create a circle visual from a geojson data source""" + + def create_options(self): if isinstance(self.style, str): style = "'{}'".format(self.style) else: @@ -146,6 +148,7 @@ def create_html(self, filename=None): options = dict( gl_js_version=GL_JS_VERSION, accessToken=self.access_token, + uid=self.uid, div_id=self.div_id, style=style, center=list(self.center), @@ -155,7 +158,7 @@ def create_html(self, filename=None): opacity=self.opacity, minzoom=self.min_zoom, maxzoom=self.max_zoom, - pitch=self.pitch, + pitch=self.pitch, bearing=self.bearing, boxZoomOn=json.dumps(self.box_zoom_on), doubleClickZoomOn=json.dumps(self.double_click_zoom_on), @@ -179,6 +182,11 @@ def create_html(self, filename=None): options.update(labelProperty='{' + self.label_property + '}') self.add_unique_template_variables(options) + return options + + def create_html(self, filename=None): + """Create a circle visual from a geojson data source""" + options = self.create_options() if filename: html = templates.format(self.template, **options) @@ -357,7 +365,7 @@ def __init__(self, :param color_stops: stops to determine heatmap color. EX. [[0, "red"], [0.5, "blue"], [1, "green"]] :param radius_stops: stops to determine heatmap radius based on zoom. EX: [[0, 1], [12, 30]] :param intensity_stops: stops to determine the heatmap intensity based on zoom. EX: [[0, 0.1], [20, 5]] - + """ super(HeatmapViz, self).__init__(data, *args, **kwargs) @@ -466,7 +474,7 @@ def __init__(self, line_color='white', line_stroke='solid', line_width=1, - height_property=None, + height_property=None, height_stops=None, height_default=0.0, height_function_type='interpolate', @@ -492,10 +500,10 @@ def __init__(self, :param height_stops: property for determining 3D extrusion height :param height_default: default height for 3D extruded polygons :param height_function_type: roperty to determine `type` used by Mapbox to assign height - + """ super(ChoroplethViz, self).__init__(data, *args, **kwargs) - + self.vector_url = vector_url self.vector_layer_name = vector_layer_name self.vector_join_property = vector_join_property @@ -529,7 +537,7 @@ def generate_vector_color_map(self): # map color to JSON feature using color_property color = color_map(row[self.color_property], self.color_stops, self.color_default) - + # link to vector feature using data_join_property (from JSON object) vector_stops.append([row[self.data_join_property], color]) @@ -538,7 +546,7 @@ def generate_vector_color_map(self): def generate_vector_height_map(self): """Generate height stops array for use with match expression in mapbox template""" vector_stops = [] - + if self.height_function_type == 'match': match_height = self.height_stops @@ -546,7 +554,7 @@ def generate_vector_height_map(self): # map height to JSON feature using height_property height = height_map(row[self.height_property], self.height_stops, self.height_default) - + # link to vector feature using data_join_property (from JSON object) vector_stops.append([row[self.data_join_property], height]) @@ -737,7 +745,7 @@ def __init__(self, """ super(LinestringViz, self).__init__(data, *args, **kwargs) - + self.vector_url = vector_url self.vector_layer_name = vector_layer_name self.vector_join_property = vector_join_property @@ -773,7 +781,7 @@ def generate_vector_color_map(self): # map color to JSON feature using color_property color = color_map(row[self.color_property], self.color_stops, self.color_default) - + # link to vector feature using data_join_property (from JSON object) vector_stops.append([row[self.data_join_property], color]) @@ -782,7 +790,7 @@ def generate_vector_color_map(self): def generate_vector_width_map(self): """Generate width stops array for use with match expression in mapbox template""" vector_stops = [] - + if self.line_width_function_type == 'match': match_width = self.line_width_stops @@ -790,7 +798,7 @@ def generate_vector_width_map(self): # map width to JSON feature using width_property width = numeric_map(row[self.line_width_property], self.line_width_stops, self.line_width_default) - + # link to vector feature using data_join_property (from JSON object) vector_stops.append([row[self.data_join_property], width]) @@ -845,7 +853,7 @@ def add_unique_template_variables(self, options): if self.color_property: options.update(dict(vectorColorStops=self.generate_vector_color_map())) - + if self.line_width_property: options.update(dict(vectorWidthStops=self.generate_vector_width_map())) @@ -855,3 +863,38 @@ def add_unique_template_variables(self, options): geojson_data=json.dumps(self.data, ensure_ascii=False), )) + +class VizCollection(MapViz): + """ + Class that can contain several vizualisations. + """ + + def __init__(self, visualisations, *args, **kwargs): + """ + Initialization of viz collection. + """ + super(VizCollection, self).__init__(None, *args, **kwargs) + self.visualisations = visualisations + + def create_html(self, filename=None): + """Create a visualization from several viz objects.""" + options = self.create_options() + viz_options = [v.create_options() for v in self.visualisations] + template_str = '{% extends "base.html" %}' + template_str += '{% block legend %}{% endblock legend %}' + template_str += '{% block map %}' + + for idx, viz in enumerate(self.visualisations): + single_viz_options = viz_options[idx] + template_layer = viz.template + '_layers' + template_str += templates.format(template_layer, **single_viz_options) + + template_str += '{% endblock map %}' + + if filename: + html = templates.format(from_string=True, template_str=template_str, **options) + with codecs.open(filename, "w", "utf-8-sig") as f: + f.write(html) + return None + else: + return templates.format(None, from_string=True, template_str=template_str, **options) From 587fe80688b10e8b738352b8750bf462028e139d Mon Sep 17 00:00:00 2001 From: Evgeni Makarov Date: Mon, 1 Oct 2018 15:23:09 +0800 Subject: [PATCH 2/3] fix for records to filename --- mapboxgl/viz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapboxgl/viz.py b/mapboxgl/viz.py index ece6f44..d9ac138 100644 --- a/mapboxgl/viz.py +++ b/mapboxgl/viz.py @@ -892,7 +892,7 @@ def create_html(self, filename=None): template_str += '{% endblock map %}' if filename: - html = templates.format(from_string=True, template_str=template_str, **options) + html = templates.format(None, from_string=True, template_str=template_str, **options) with codecs.open(filename, "w", "utf-8-sig") as f: f.write(html) return None From 2e48b44511bd90c714fdf8df88c8ab216c0b1d0a Mon Sep 17 00:00:00 2001 From: Evgeni Makarov Date: Tue, 2 Oct 2018 11:46:29 +0800 Subject: [PATCH 3/3] choropletch layers added --- mapboxgl/templates/choropleth.html | 121 +--------------------- mapboxgl/templates/choropleth_layers.html | 120 +++++++++++++++++++++ 2 files changed, 121 insertions(+), 120 deletions(-) create mode 100644 mapboxgl/templates/choropleth_layers.html diff --git a/mapboxgl/templates/choropleth.html b/mapboxgl/templates/choropleth.html index 00e2f23..96d5cb2 100644 --- a/mapboxgl/templates/choropleth.html +++ b/mapboxgl/templates/choropleth.html @@ -27,125 +27,6 @@ {% block map %} - map.on('style.load', function() { - - {% block choropleth %} - - // Add geojson data source - map.addSource("data" + "_" + "{{uid}}", { - "type": "geojson", - "data": {{ geojson_data }}, - "buffer": 1, - "maxzoom": 14 - }); - - // Add data layer - map.addLayer({ - "id": "choropleth-fill" + "_" + "{{uid}}", - "source": "data" + "_" + "{{uid}}", - "type": "fill", - "paint": { - "fill-color": generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}"), - "fill-opacity": {{ opacity }} - } - }, "{{ belowLayer }}" ); - - // Add border layer - map.addLayer({ - "id": "choropleth-line" + "_" + "{{uid}}", - "source": "data" + "_" + "{{uid}}", - "type": "line", - "layout": { - "line-join": "round", - "line-cap": "round" - }, - "paint": { - {% if lineDashArray %} - "line-dasharray": {{ lineDashArray }}, - {% endif %} - "line-color": "{{ lineColor }}", - "line-width": {{ lineWidth }}, - "line-opacity": {{ opacity }} - } - }, "{{ belowLayer }}" ); - - // Add label layer - map.addLayer({ - "id": "choropleth-label" + "_" + "{{uid}}", - "source": "data" + "_" + "{{uid}}", - "type": "symbol", - "layout": { - {% if labelProperty %} - "text-field": "{{ labelProperty }}", - {% endif %} - "text-size" : generateInterpolateExpression('zoom', [[0,8],[22,16]] ), - "text-offset": [0,-1] - }, - "paint": { - "text-halo-color": "white", - "text-halo-width": 1 - } - }, "{{ belowLayer }}" ); - - // Optional extrusion layer - {% if extrudeChoropleth %} - - map.addLayer({ - id: "choropleth-extrusion" + "_" + "{{uid}}", - type: "fill-extrusion", - source: "data" + "_" + "{{uid}}", - paint: { - "fill-extrusion-opacity": {{ opacity }}, - "fill-extrusion-color": generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}"), - "fill-extrusion-height": generatePropertyExpression("{{ heightType }}", "{{ heightProperty }}", {{ heightStops }}, {{ defaultHeight }}), - } - }, "{{ belowLayer }}"); - - {% endif %} - - - {% endblock choropleth %} - - // Create a popup - var popup = new mapboxgl.Popup({ - closeButton: false, - closeOnClick: false - }); - - {% block choropleth_popup %} - - // Show the popup on mouseover - map.on('mousemove', 'choropleth-fill' + "_" + "{{uid}}", function(e) { - map.getCanvas().style.cursor = 'pointer'; - - let f = e.features[0]; - let popup_html = '
    '; - - for (key in f.properties) { - popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' - } - - popup_html += '
    ' - popup.setLngLat(e.lngLat) - .setHTML(popup_html) - .addTo(map); - }); - - {% endblock choropleth_popup %} - - map.on('mouseleave', 'choropleth-fill' + "_" + "{{uid}}", function() { - map.getCanvas().style.cursor = ''; - popup.remove(); - }); - - // Fly to on click - map.on('click', 'choropleth-fill' + "_" + "{{uid}}", function(e) { - map.flyTo({ - center: e.lngLat, - zoom: map.getZoom() + 1 - }); - }); - - }); + {% include "choropleth_layers.html" %} {% endblock map %} diff --git a/mapboxgl/templates/choropleth_layers.html b/mapboxgl/templates/choropleth_layers.html new file mode 100644 index 0000000..5bc21e4 --- /dev/null +++ b/mapboxgl/templates/choropleth_layers.html @@ -0,0 +1,120 @@ +map.on('style.load', function() { + + {% block choropleth %} + + // Add geojson data source + map.addSource("data" + "_" + "{{uid}}", { + "type": "geojson", + "data": {{ geojson_data }}, + "buffer": 1, + "maxzoom": 14 + }); + + // Add data layer + map.addLayer({ + "id": "choropleth-fill" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", + "type": "fill", + "paint": { + "fill-color": generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}"), + "fill-opacity": {{ opacity }} + } + }, "{{ belowLayer }}" ); + + // Add border layer + map.addLayer({ + "id": "choropleth-line" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", + "type": "line", + "layout": { + "line-join": "round", + "line-cap": "round" + }, + "paint": { + {% if lineDashArray %} + "line-dasharray": {{ lineDashArray }}, + {% endif %} + "line-color": "{{ lineColor }}", + "line-width": {{ lineWidth }}, + "line-opacity": {{ opacity }} + } + }, "{{ belowLayer }}" ); + + // Add label layer + map.addLayer({ + "id": "choropleth-label" + "_" + "{{uid}}", + "source": "data" + "_" + "{{uid}}", + "type": "symbol", + "layout": { + {% if labelProperty %} + "text-field": "{{ labelProperty }}", + {% endif %} + "text-size" : generateInterpolateExpression('zoom', [[0,8],[22,16]] ), + "text-offset": [0,-1] + }, + "paint": { + "text-halo-color": "white", + "text-halo-width": 1 + } + }, "{{ belowLayer }}" ); + + // Optional extrusion layer + {% if extrudeChoropleth %} + + map.addLayer({ + id: "choropleth-extrusion" + "_" + "{{uid}}", + type: "fill-extrusion", + source: "data" + "_" + "{{uid}}", + paint: { + "fill-extrusion-opacity": {{ opacity }}, + "fill-extrusion-color": generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}"), + "fill-extrusion-height": generatePropertyExpression("{{ heightType }}", "{{ heightProperty }}", {{ heightStops }}, {{ defaultHeight }}), + } + }, "{{ belowLayer }}"); + + {% endif %} + + + {% endblock choropleth %} + + // Create a popup + var popup = new mapboxgl.Popup({ + closeButton: false, + closeOnClick: false + }); + + {% block choropleth_popup %} + + // Show the popup on mouseover + map.on('mousemove', 'choropleth-fill' + "_" + "{{uid}}", function(e) { + map.getCanvas().style.cursor = 'pointer'; + + let f = e.features[0]; + let popup_html = '
    '; + + for (key in f.properties) { + popup_html += '
  • ' + key + ': ' + f.properties[key] + '
  • ' + } + + popup_html += '
    ' + popup.setLngLat(e.lngLat) + .setHTML(popup_html) + .addTo(map); + }); + + {% endblock choropleth_popup %} + + map.on('mouseleave', 'choropleth-fill' + "_" + "{{uid}}", function() { + map.getCanvas().style.cursor = ''; + popup.remove(); + }); + + // Fly to on click + map.on('click', 'choropleth-fill' + "_" + "{{uid}}", function(e) { + map.flyTo({ + center: e.lngLat, + zoom: map.getZoom() + 1 + }); + }); + +});