Skip to content

Commit

Permalink
Update GL JS for 3D ray-picking mouse interaction support, bump to v0…
Browse files Browse the repository at this point in the history
….10.1 (#145)

* update GL JS version, update default style to light-v10

* update hoverLayer to be extrusion, to take advantage of raypicking support in 3D extrusions now

* update to v0.10.1

* add hover/highlight style for choropleth viz using feature state and geojson source

* require pytest version >=3.6

* added support for highlight hover on vector-choropleth layers

* update all templates to use hover/highlight color

* update highlight_color param on all compatible viz types

* add param notes for doc updates on highlight_color

* Update viz.py
  • Loading branch information
ryanbaumann committed Feb 14, 2019
1 parent 6e95434 commit 941525a
Show file tree
Hide file tree
Showing 15 changed files with 466 additions and 406 deletions.
2 changes: 1 addition & 1 deletion mapboxgl/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .viz import CircleViz, GraduatedCircleViz, HeatmapViz, ClusteredCircleViz, ImageViz, RasterTilesViz, ChoroplethViz, LinestringViz

__version__ = "0.10.0"
__version__ = "0.10.1"
__all__ = ['CircleViz', 'GraduatedCircleViz', 'HeatmapViz', 'ClusteredCircleViz', 'ImageViz', 'RasterTilesViz', 'ChoroplethViz', 'LinestringViz']
26 changes: 10 additions & 16 deletions mapboxgl/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@

mapboxgl.accessToken = '{{ accessToken }}';

var transformRequest = function(url, resourceType) {
const isMapboxRequest = url.slice(8, 22) === 'api.mapbox.com' ||
url.slice(10, 26) === 'tiles.mapbox.com';

return {
url: isMapboxRequest ? url.replace('?', '?pluginName=PythonMapboxgl&') : url
}
};

var map = new mapboxgl.Map({
container: 'map',
attributionControl: false,
Expand All @@ -17,22 +26,7 @@
doubleClickZoom: {{ doubleClickZoomOn|safe }},
boxZoom: {{ boxZoomOn|safe }},
preserveDrawingBuffer: {{ preserveDrawingBuffer|safe }},
transformRequest: (url, resourceType) => {
if ( url.slice(0,22) == 'https://api.mapbox.com' ||
url.slice(0,26) == 'https://a.tiles.mapbox.com' ||
url.slice(0,26) == 'https://b.tiles.mapbox.com' ||
url.slice(0,26) == 'https://c.tiles.mapbox.com' ||
url.slice(0,26) == 'https://d.tiles.mapbox.com') {
//Add Mapboxgl-Jupyter Plugin identifier for Mapbox API traffic
return {
url: [url.slice(0, url.indexOf("?")+1), "pluginName=PythonMapboxgl&", url.slice(url.indexOf("?")+1)].join('')
}
}
else {
//Do not transform URL for non Mapbox GET requests
return {url: url}
}
},
transformRequest: transformRequest
});

{% block attribution %}
Expand Down
81 changes: 46 additions & 35 deletions mapboxgl/templates/choropleth.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@
"source": "data",
"type": "fill",
"paint": {
"fill-color": generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}"),
"fill-color": ["case",
["boolean", ["feature-state", "hover"], false],
"{{ highlightColor }}",
generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}")],
"fill-opacity": {{ opacity }}
}
}, "{{ belowLayer }}" );
Expand All @@ -63,7 +66,10 @@
{% if lineDashArray %}
"line-dasharray": {{ lineDashArray }},
{% endif %}
"line-color": "{{ lineColor }}",
"line-color": ["case",
["boolean", ["feature-state", "hover"], false],
"{{ highlightColor }}",
"{{ lineColor }}"],
"line-width": {{ lineWidth }},
"line-opacity": {{ opacity }}
}
Expand All @@ -84,7 +90,9 @@
"paint": {
"text-halo-color": "{{ labelHaloColor }}",
"text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]),
"text-color": "{{ labelColor }}"
"text-color": ["case",
["boolean", ["feature-state", "hover"], false],
"{{ highlightColor }}", "{{ labelColor }}"]
}
}, "{{ belowLayer }}" );

Expand All @@ -97,7 +105,10 @@
source: "data",
paint: {
"fill-extrusion-opacity": {{ opacity }},
"fill-extrusion-color": generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}"),
"fill-extrusion-color": ["case",
["boolean", ["feature-state", "hover"], false],
"{{ highlightColor }}",
generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}")],
"fill-extrusion-height": generatePropertyExpression("{{ heightType }}", "{{ heightProperty }}", {{ heightStops }}, {{ defaultHeight }}),
}
}, "{{ belowLayer }}");
Expand All @@ -107,6 +118,7 @@
{% endblock choropleth %}

// Popups
var interactionLayer = {% if extrudeChoropleth %} 'choropleth-extrusion' {% else %} 'choropleth-fill' {% endif %};
{% if popupOpensOnHover %}
var popupAction = 'mousemove',
popupSettings = {
Expand All @@ -127,45 +139,44 @@
{% block choropleth_popup %}

// Show the popup on mouseover
map.on(popupAction, 'choropleth-fill', function(e) {
var hoveredStateId = 0;

{% if popupOpensOnHover %}
map.on(popupAction, function(e) {
var features = map.queryRenderedFeatures(e.point, {layers: [interactionLayer, 'choropleth-label'] });

if (features.length > 0) {
map.getCanvas().style.cursor = 'pointer';
{% endif %}

let f = e.features[0];
let popup_html = '<div>';
var f = features[0];
newHoveredStateId = f.id;
if (newHoveredStateId != hoveredStateId) {
map.removeFeatureState({source: 'data', id: hoveredStateId});
hoveredStateId = newHoveredStateId;
}
map.setFeatureState({source: 'data', id: hoveredStateId}, { hover: true});
let popup_html = '<div>';

for (key in f.properties) {
popup_html += '<li><b> ' + key + '</b>: ' + f.properties[key] + ' </li>'
}
for (key in f.properties) {
popup_html += '<li><b> ' + key + '</b>: ' + f.properties[key] + ' </li>'
}

popup_html += '</div>'
popup.setLngLat(e.lngLat)
.setHTML(popup_html)
.addTo(map);
popup_html += '</div>'
popup.setLngLat(e.lngLat)
.setHTML(popup_html)
.addTo(map);
}
else {
map.getCanvas().style.cursor = '';
popup.remove();
map.removeFeatureState({source: 'data', id: hoveredStateId});
}
});

{% endblock choropleth_popup %}

// change cursor to pointer when mouse is over the choropleth feature layer
map.on('mouseenter', 'choropleth-fill', function () {
map.getCanvas().style.cursor = 'pointer';
});

// reset cursor to pointer when mouse leaves the choropleth feature layer
map.on('mouseleave', 'choropleth-fill', function() {
map.getCanvas().style.cursor = '';
{% if popupOpensOnHover %}
popup.remove();
{% endif %}
});

// Fly to on click
map.on('click', 'choropleth-fill', function(e) {
map.flyTo({
center: e.lngLat,
zoom: map.getZoom() + 1
// Fly to selected feature on click
map.on('click', interactionLayer, function(e) {
map.easeTo({
center: e.lngLat
});
});

Expand Down
82 changes: 47 additions & 35 deletions mapboxgl/templates/circle.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"type": "geojson",
"data": {{ geojson_data }},
"buffer": 1,
"maxzoom": 14
"maxzoom": 14,
"generateId": true
});

map.addLayer({
Expand All @@ -46,7 +47,10 @@
"paint": {
"text-halo-color": "{{ labelHaloColor }}",
"text-halo-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ labelHaloWidth }}], [18,5* {{ labelHaloWidth }}]]),
"text-color": "{{ labelColor }}"
"text-color": ["case",
["boolean", ["feature-state", "hover"], false],
"{{ highlightColor }}",
"{{ labelColor }}"]
}
}, "{{belowLayer}}" )

Expand All @@ -58,12 +62,20 @@
"minzoom": {{ minzoom }},
"paint": {
{% if colorProperty %}
"circle-color": generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}" ),
"circle-color": ["case",
["boolean", ["feature-state", "hover"], false],
"{{ highlightColor }}",
generatePropertyExpression("{{ colorType }}", "{{ colorProperty }}", {{ colorStops }}, "{{ defaultColor }}" )],
{% else %}
"circle-color": "{{ defaultColor }}",
"circle-color": ["case",
["boolean", ["feature-state", "hover"], false],
"{{ highlightColor }}", "{{ defaultColor }}"],
{% endif %}
"circle-radius" : generatePropertyExpression('interpolate', 'zoom', [[0,{{ radius }}], [22,10 * {{ radius }}]]),
"circle-stroke-color": "{{ strokeColor }}",
"circle-stroke-color": ["case",
["boolean", ["feature-state", "hover"], false],
"{{ highlightColor }}",
"{{ strokeColor }}"],
"circle-stroke-width": generatePropertyExpression('interpolate', 'zoom', [[0,{{ strokeWidth }}], [18,5* {{ strokeWidth }}]]),
"circle-opacity" : {{ opacity }},
"circle-stroke-opacity" : {{ opacity }}
Expand Down Expand Up @@ -91,48 +103,48 @@
var popup = new mapboxgl.Popup(popupSettings);

{% block circle_popup %}

var hoveredStateId = 0;

// Show the popup on mouseover
map.on(popupAction, 'circle', function(e) {
map.on(popupAction, function(e) {

{% if popupOpensOnHover %}
map.getCanvas().style.cursor = 'pointer';
{% endif %}

let f = e.features[0];
let popup_html = '<div><li><b>Location</b>: ' + f.geometry.coordinates[0].toPrecision(6) +
', ' + f.geometry.coordinates[1].toPrecision(6) + '</li>';
var features = map.queryRenderedFeatures(e.point, {layers: ['circle', 'label'] });

for (key in f.properties) {
popup_html += '<li><b> ' + key + '</b>: ' + f.properties[key] + ' </li>'
if (features.length > 0) {
map.getCanvas().style.cursor = 'pointer';
var f = features[0];
newHoveredStateId = f.id;
if (newHoveredStateId != hoveredStateId) {
map.removeFeatureState({source: 'data', id: hoveredStateId});
hoveredStateId = newHoveredStateId;
}
map.setFeatureState({source: 'data', id: hoveredStateId}, { hover: true});
let popup_html = '<div><li><b>Location</b>: ' + f.geometry.coordinates[0].toPrecision(6) +
', ' + f.geometry.coordinates[1].toPrecision(6) + '</li>';

for (key in f.properties) {
popup_html += '<li><b> ' + key + '</b>: ' + f.properties[key] + ' </li>'
}

popup_html += '</div>'
popup.setLngLat(e.lngLat)
.setHTML(popup_html)
.addTo(map);
}
else {
map.getCanvas().style.cursor = '';
popup.remove();
map.removeFeatureState({source: 'data', id: hoveredStateId});
}

popup_html += '</div>'
popup.setLngLat(e.features[0].geometry.coordinates)
.setHTML(popup_html)
.addTo(map);
});

{% endblock circle_popup %}

// change cursor to pointer when mouse is over the circle feature layer
map.on('mouseenter', 'circle', function () {
map.getCanvas().style.cursor = 'pointer';
});

// reset cursor to pointer when mouse leaves the circle feature layer
map.on('mouseleave', 'circle', function() {
map.getCanvas().style.cursor = '';
{% if popupOpensOnHover %}
popup.remove();
{% endif %}
});

// Fly to on click
map.on('click', 'circle', function(e) {
map.easeTo({
center: e.features[0].geometry.coordinates,
zoom: map.getZoom() + 1
center: e.features[0].geometry.coordinates
});
});
});
Expand Down

0 comments on commit 941525a

Please sign in to comment.