Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Adding maps #71

Merged
merged 4 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions plugins/plotly-express/src/deephaven/plot/express/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@
pie,
layer,
make_subplots,
scatter_geo,
scatter_mapbox,
density_mapbox,
line_geo,
line_mapbox,
)

from .data import data_generators
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@
# in other cases, the color would already be calculated so this argument
# would not be set
"color",
"lat",
"lon",
"locations",
}
DATA_ARGS.update(DATA_LIST_ARGS)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
from .pie import pie
from ._layer import layer
from .subplots import make_subplots
from .maps import scatter_geo, scatter_mapbox, density_mapbox, line_geo, line_mapbox
734 changes: 734 additions & 0 deletions plugins/plotly-express/src/deephaven/plot/express/plots/maps.py

Large diffs are not rendered by default.

22 changes: 20 additions & 2 deletions plugins/plotly-express/src/js/src/PlotlyExpressChartModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,26 @@ export class PlotlyExpressChartModel extends ChartModel {
}
}

has3D(): boolean {
return this.data.some(({ type }) => type != null && type.includes('3d'));
shouldPauseOnUserInteraction(): boolean {
return (
this.hasScene() || this.hasGeo() || this.hasMapbox() || this.hasPolar()
);
}

hasScene(): boolean {
return this.data.some(d => 'scene' in d && d.scene != null);
}

hasGeo(): boolean {
return this.data.some(d => 'geo' in d && d.geo != null);
}

hasMapbox(): boolean {
return this.data.some(({ type }) => type?.includes('mapbox'));
}

hasPolar(): boolean {
return this.data.some(({ type }) => type?.includes('polar'));
}

getPlotWidth(): number {
Expand Down
10 changes: 8 additions & 2 deletions plugins/plotly-express/src/js/src/PlotlyExpressChartPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,14 @@ function PlotlyExpressChartPanel(props: PlotlyExpressChartPanelProps) {
}, [dh, fetch]);

useEffect(
function handle3DTicks() {
if (!model || !containerRef.current || !model.has3D()) {
function handleSceneTicks() {
// Plotly scenes and geo views reset when our data ticks
// Pause rendering data updates when the user is manipulating a scene
if (
!model ||
!containerRef.current ||
!model.shouldPauseOnUserInteraction()
) {
return;
}

Expand Down
232 changes: 232 additions & 0 deletions plugins/plotly-express/test/deephaven/plot/express/plots/test_maps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import unittest

from ..BaseTest import BaseTestCase


class MapTestCase(BaseTestCase):
def setUp(self) -> None:
from deephaven import new_table
from deephaven.column import int_col

self.source = new_table(
[
int_col("lat", [1, 2, 2, 3, 3, 3, 4, 4, 5]),
int_col("lon", [1, 2, 2, 3, 3, 3, 4, 4, 5]),
int_col("z", [1, 2, 2, 3, 3, 3, 4, 4, 5]),
]
)

def test_basic_scatter_geo(self):
import src.deephaven.plot.express as dx
from deephaven.constants import NULL_INT

chart = dx.scatter_geo(self.source, lat="lat", lon="lon").to_dict(self.exporter)
plotly, deephaven = chart["plotly"], chart["deephaven"]

# pop template as we currently do not modify it
plotly["layout"].pop("template")

expected_data = [
{
"featureidkey": "id",
"geo": "geo",
"hovertemplate": "lat=%{lat}<br>lon=%{lon}<extra></extra>",
"lat": [NULL_INT],
"legendgroup": "",
"lon": [NULL_INT],
"marker": {"color": "#636efa", "symbol": "circle"},
"mode": "markers",
"name": "",
"showlegend": False,
"type": "scattergeo",
}
]

self.assertEqual(plotly["data"], expected_data)

expected_layout = {
"geo": {"domain": {"x": [0.0, 1.0], "y": [0.0, 1.0]}, "fitbounds": False},
"legend": {"tracegroupgap": 0},
"margin": {"t": 60},
}

self.assertEqual(plotly["layout"], expected_layout)

def test_basic_scatter_mapbox(self):
import src.deephaven.plot.express as dx
from deephaven.constants import NULL_INT

chart = dx.scatter_mapbox(self.source, lat="lat", lon="lon").to_dict(
self.exporter
)
plotly, deephaven = chart["plotly"], chart["deephaven"]

# pop template as we currently do not modify it
plotly["layout"].pop("template")

expected_data = [
{
"hovertemplate": "lat=%{lat}<br>lon=%{lon}<extra></extra>",
"lat": [NULL_INT],
"legendgroup": "",
"lon": [NULL_INT],
"marker": {"color": "#636efa"},
"mode": "markers",
"name": "",
"showlegend": False,
"subplot": "mapbox",
"type": "scattermapbox",
}
]

self.assertEqual(plotly["data"], expected_data)

expected_layout = {
"legend": {"tracegroupgap": 0},
"mapbox": {
"center": {"lat": NULL_INT, "lon": NULL_INT},
"domain": {"x": [0.0, 1.0], "y": [0.0, 1.0]},
"style": "open-street-map",
"zoom": 8,
},
"margin": {"t": 60},
}

self.assertEqual(plotly["layout"], expected_layout)

def test_basic_line_geo(self):
import src.deephaven.plot.express as dx
from deephaven.constants import NULL_INT

chart = dx.line_geo(self.source, lat="lat", lon="lon").to_dict(self.exporter)
plotly, deephaven = chart["plotly"], chart["deephaven"]

# pop template as we currently do not modify it
plotly["layout"].pop("template")

expected_data = [
{
"featureidkey": "id",
"geo": "geo",
"hovertemplate": "lat=%{lat}<br>lon=%{lon}<extra></extra>",
"lat": [NULL_INT],
"legendgroup": "",
"line": {"color": "#636efa", "dash": "solid"},
"lon": [NULL_INT],
"marker": {"symbol": "circle"},
"mode": "lines",
"name": "",
"showlegend": False,
"type": "scattergeo",
}
]

self.assertEqual(plotly["data"], expected_data)

expected_layout = {
"geo": {"domain": {"x": [0.0, 1.0], "y": [0.0, 1.0]}, "fitbounds": False},
"legend": {"tracegroupgap": 0},
"margin": {"t": 60},
}

self.assertEqual(plotly["layout"], expected_layout)

def test_basic_line_mapbox(self):
import src.deephaven.plot.express as dx
from deephaven.constants import NULL_INT

chart = dx.line_mapbox(self.source, lat="lat", lon="lon").to_dict(self.exporter)
plotly, deephaven = chart["plotly"], chart["deephaven"]

# pop template as we currently do not modify it
plotly["layout"].pop("template")

expected_data = [
{
"hovertemplate": "lat=%{lat}<br>lon=%{lon}<extra></extra>",
"lat": [NULL_INT],
"legendgroup": "",
"line": {"color": "#636efa"},
"lon": [NULL_INT],
"mode": "lines",
"name": "",
"showlegend": False,
"subplot": "mapbox",
"type": "scattermapbox",
}
]

self.assertEqual(plotly["data"], expected_data)

expected_layout = {
"legend": {"tracegroupgap": 0},
"mapbox": {
"center": {"lat": NULL_INT, "lon": NULL_INT},
"domain": {"x": [0.0, 1.0], "y": [0.0, 1.0]},
"style": "open-street-map",
"zoom": 8,
},
"margin": {"t": 60},
}

self.assertEqual(plotly["layout"], expected_layout)

def test_basic_density_mapbox(self):
import src.deephaven.plot.express as dx
from deephaven.constants import NULL_INT

chart = dx.density_mapbox(self.source, lat="lat", lon="lon", z="z").to_dict(
self.exporter
)
plotly, deephaven = chart["plotly"], chart["deephaven"]

# pop template as we currently do not modify it
plotly["layout"].pop("template")

expected_data = [
{
"coloraxis": "coloraxis",
"hovertemplate": "lat=%{lat}<br>lon=%{lon}<br>z=%{z}<extra></extra>",
"lat": [NULL_INT],
"lon": [NULL_INT],
"name": "",
"radius": 30,
"subplot": "mapbox",
"z": [-2147483648],
"type": "densitymapbox",
}
]

self.assertEqual(plotly["data"], expected_data)

expected_layout = {
"coloraxis": {
"colorbar": {"title": {"text": "z"}},
"colorscale": [
[0.0, "#0d0887"],
[0.1111111111111111, "#46039f"],
[0.2222222222222222, "#7201a8"],
[0.3333333333333333, "#9c179e"],
[0.4444444444444444, "#bd3786"],
[0.5555555555555556, "#d8576b"],
[0.6666666666666666, "#ed7953"],
[0.7777777777777778, "#fb9f3a"],
[0.8888888888888888, "#fdca26"],
[1.0, "#f0f921"],
],
},
"legend": {"tracegroupgap": 0},
"mapbox": {
"center": {"lat": NULL_INT, "lon": NULL_INT},
"domain": {"x": [0.0, 1.0], "y": [0.0, 1.0]},
"style": "open-street-map",
"zoom": 8,
},
"margin": {"t": 60},
}

self.assertEqual(plotly["layout"], expected_layout)


if __name__ == "__main__":
unittest.main()