From 72bacb569b5e571f3af9ada082e80dd948321d75 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Tue, 6 Aug 2024 17:39:57 -0400 Subject: [PATCH] new map subplots & traces to px, etc. --- packages/python/plotly/plotly/_subplots.py | 5 +- .../python/plotly/plotly/express/__init__.py | 8 + .../plotly/plotly/express/_chart_types.py | 147 ++++++++++++++++++ .../python/plotly/plotly/express/_core.py | 46 +++++- packages/python/plotly/plotly/express/_doc.py | 7 + packages/python/plotly/plotly/subplots.py | 1 + .../plotly/templategen/utils/__init__.py | 1 + 7 files changed, 207 insertions(+), 8 deletions(-) diff --git a/packages/python/plotly/plotly/_subplots.py b/packages/python/plotly/plotly/_subplots.py index 51898363975..37c15958a5f 100644 --- a/packages/python/plotly/plotly/_subplots.py +++ b/packages/python/plotly/plotly/_subplots.py @@ -8,7 +8,7 @@ # little differently. import collections -_single_subplot_types = {"scene", "geo", "polar", "ternary", "mapbox"} +_single_subplot_types = {"scene", "geo", "polar", "ternary", "map", "mapbox"} _subplot_types = set.union(_single_subplot_types, {"xy", "domain"}) # For most subplot types, a trace is associated with a particular subplot @@ -20,7 +20,7 @@ # the trace property is just named `subplot`. For example setting # the `scatterpolar.subplot` property to `polar3` associates the scatterpolar # trace with the third polar subplot in the figure -_subplot_prop_named_subplot = {"polar", "ternary", "mapbox"} +_subplot_prop_named_subplot = {"polar", "ternary", "map", "mapbox"} # Named tuple to hold an xaxis/yaxis pair that represent a single subplot @@ -150,6 +150,7 @@ def make_subplots( - 'scene': 3D Cartesian subplot for scatter3d, cone, etc. - 'polar': Polar subplot for scatterpolar, barpolar, etc. - 'ternary': Ternary subplot for scatterternary + - 'map': Map subplot for scattermap - 'mapbox': Mapbox subplot for scattermapbox - 'domain': Subplot type for traces that are individually positioned. pie, parcoords, parcats, etc. diff --git a/packages/python/plotly/plotly/express/__init__.py b/packages/python/plotly/plotly/express/__init__.py index 4dd37f64fc3..935d6745788 100644 --- a/packages/python/plotly/plotly/express/__init__.py +++ b/packages/python/plotly/plotly/express/__init__.py @@ -17,12 +17,14 @@ scatter_3d, scatter_polar, scatter_ternary, + scatter_map, scatter_mapbox, scatter_geo, line, line_3d, line_polar, line_ternary, + line_map, line_mapbox, line_geo, area, @@ -46,7 +48,9 @@ icicle, funnel, funnel_area, + choropleth_map, choropleth_mapbox, + density_map, density_mapbox, ) @@ -67,16 +71,19 @@ "scatter_3d", "scatter_polar", "scatter_ternary", + "scatter_map", "scatter_mapbox", "scatter_geo", "scatter_matrix", "density_contour", "density_heatmap", + "density_map", "density_mapbox", "line", "line_3d", "line_polar", "line_ternary", + "line_map", "line_mapbox", "line_geo", "parallel_coordinates", @@ -91,6 +98,7 @@ "histogram", "ecdf", "choropleth", + "choropleth_map", "choropleth_mapbox", "pie", "sunburst", diff --git a/packages/python/plotly/plotly/express/_chart_types.py b/packages/python/plotly/plotly/express/_chart_types.py index 74cdf84fa49..f8d39e2f41c 100644 --- a/packages/python/plotly/plotly/express/_chart_types.py +++ b/packages/python/plotly/plotly/express/_chart_types.py @@ -1211,6 +1211,153 @@ def line_geo( line_geo.__doc__ = make_docstring(line_geo) +def scatter_map( + data_frame=None, + lat=None, + lon=None, + color=None, + text=None, + hover_name=None, + hover_data=None, + custom_data=None, + size=None, + animation_frame=None, + animation_group=None, + category_orders=None, + labels=None, + color_discrete_sequence=None, + color_discrete_map=None, + color_continuous_scale=None, + range_color=None, + color_continuous_midpoint=None, + opacity=None, + size_max=None, + zoom=8, + center=None, + map_style=None, + title=None, + template=None, + width=None, + height=None, +) -> go.Figure: + """ + In a Map scatter plot, each row of `data_frame` is represented by a + symbol mark on the map. + """ + return make_figure(args=locals(), constructor=go.Scattermap) + + +scatter_map.__doc__ = make_docstring(scatter_map) + + +def choropleth_map( + data_frame=None, + geojson=None, + featureidkey=None, + locations=None, + color=None, + hover_name=None, + hover_data=None, + custom_data=None, + animation_frame=None, + animation_group=None, + category_orders=None, + labels=None, + color_discrete_sequence=None, + color_discrete_map=None, + color_continuous_scale=None, + range_color=None, + color_continuous_midpoint=None, + opacity=None, + zoom=8, + center=None, + map_style=None, + title=None, + template=None, + width=None, + height=None, +) -> go.Figure: + """ + In a Map choropleth map, each row of `data_frame` is represented by a + colored region on the map. + """ + return make_figure(args=locals(), constructor=go.Choroplethmap) + + +choropleth_map.__doc__ = make_docstring(choropleth_map) + + +def density_map( + data_frame=None, + lat=None, + lon=None, + z=None, + hover_name=None, + hover_data=None, + custom_data=None, + animation_frame=None, + animation_group=None, + category_orders=None, + labels=None, + color_continuous_scale=None, + range_color=None, + color_continuous_midpoint=None, + opacity=None, + zoom=8, + center=None, + map_style=None, + radius=None, + title=None, + template=None, + width=None, + height=None, +) -> go.Figure: + """ + In a Map density map, each row of `data_frame` contributes to the intensity of + the color of the region around the corresponding point on the map + """ + return make_figure( + args=locals(), constructor=go.Densitymap, trace_patch=dict(radius=radius) + ) + + +density_map.__doc__ = make_docstring(density_map) + + +def line_map( + data_frame=None, + lat=None, + lon=None, + color=None, + text=None, + hover_name=None, + hover_data=None, + custom_data=None, + line_group=None, + animation_frame=None, + animation_group=None, + category_orders=None, + labels=None, + color_discrete_sequence=None, + color_discrete_map=None, + zoom=8, + center=None, + map_style=None, + title=None, + template=None, + width=None, + height=None, +) -> go.Figure: + """ + In a Map line plot, each row of `data_frame` is represented as + vertex of a polyline mark on the map. + """ + return make_figure(args=locals(), constructor=go.Scattermap) + + +line_map.__doc__ = make_docstring(line_map) + + def scatter_mapbox( data_frame=None, lat=None, diff --git a/packages/python/plotly/plotly/express/_core.py b/packages/python/plotly/plotly/express/_core.py index 36c57055331..9a79810506d 100644 --- a/packages/python/plotly/plotly/express/_core.py +++ b/packages/python/plotly/plotly/express/_core.py @@ -426,7 +426,11 @@ def make_trace_kwargs(args, trace_spec, trace_data, mapping_labels, sizeref): # as a list of row lists, which is what we want trace_patch["customdata"] = trace_data[customdata_cols] elif attr_name == "color": - if trace_spec.constructor in [go.Choropleth, go.Choroplethmapbox]: + if trace_spec.constructor in [ + go.Choropleth, + go.Choroplethmap, + go.Choroplethmapbox, + ]: trace_patch["z"] = trace_data[attr_value] trace_patch["coloraxis"] = "coloraxis1" mapping_labels[attr_label] = "%{z}" @@ -532,6 +536,9 @@ def configure_axes(args, constructor, fig, orders): go.Scatterpolar: configure_polar_axes, go.Scatterpolargl: configure_polar_axes, go.Barpolar: configure_polar_axes, + go.Scattermap: configure_map, + go.Choroplethmap: configure_map, + go.Densitymap: configure_map, go.Scattermapbox: configure_mapbox, go.Choroplethmapbox: configure_mapbox, go.Densitymapbox: configure_mapbox, @@ -739,6 +746,20 @@ def configure_mapbox(args, fig, orders): ) +def configure_map(args, fig, orders): + center = args["center"] + if not center and "lat" in args and "lon" in args: + center = dict( + lat=args["data_frame"][args["lat"]].mean(), + lon=args["data_frame"][args["lon"]].mean(), + ) + fig.update_maps( + center=center, + zoom=args["zoom"], + style=args["map_style"], + ) + + def configure_geo(args, fig, orders): fig.update_geos( center=args["center"], @@ -1911,7 +1932,7 @@ def infer_config(args, constructor, trace_patch, layout_patch): else: trace_patch["texttemplate"] = "%{" + letter + ":" + args["text_auto"] + "}" - if constructor in [go.Histogram2d, go.Densitymapbox]: + if constructor in [go.Histogram2d, go.Densitymap, go.Densitymapbox]: show_colorbar = True trace_patch["coloraxis"] = "coloraxis1" @@ -1919,7 +1940,13 @@ def infer_config(args, constructor, trace_patch, layout_patch): if args["opacity"] is None: if "barmode" in args and args["barmode"] == "overlay": trace_patch["marker"] = dict(opacity=0.5) - elif constructor in [go.Densitymapbox, go.Pie, go.Funnel, go.Funnelarea]: + elif constructor in [ + go.Densitymap, + go.Densitymapbox, + go.Pie, + go.Funnel, + go.Funnelarea, + ]: trace_patch["opacity"] = args["opacity"] else: trace_patch["marker"] = dict(opacity=args["opacity"]) @@ -1937,7 +1964,7 @@ def infer_config(args, constructor, trace_patch, layout_patch): modes.add("lines") trace_patch["mode"] = "+".join(sorted(modes)) elif constructor != go.Splom and ( - "symbol" in args or constructor == go.Scattermapbox + "symbol" in args or constructor in [go.Scattermap, go.Scattermapbox] ): trace_patch["mode"] = "markers" + ("+text" if args["text"] else "") @@ -2154,7 +2181,9 @@ def make_figure(args, constructor, trace_patch=None, layout_patch=None): go.Parcats, go.Parcoords, go.Choropleth, + go.Choroplethmap, go.Choroplethmapbox, + go.Densitymap, go.Densitymapbox, go.Histogram2d, go.Sunburst, @@ -2198,7 +2227,8 @@ def make_figure(args, constructor, trace_patch=None, layout_patch=None): ): trace.update(marker=dict(color=m.val_map[val])) elif ( - trace_spec.constructor in [go.Choropleth, go.Choroplethmapbox] + trace_spec.constructor + in [go.Choropleth, go.Choroplethmap, go.Choroplethmapbox] and m.variable == "color" ): trace.update( @@ -2281,7 +2311,11 @@ def make_figure(args, constructor, trace_patch=None, layout_patch=None): ) if show_colorbar: - colorvar = "z" if constructor in [go.Histogram2d, go.Densitymapbox] else "color" + colorvar = ( + "z" + if constructor in [go.Histogram2d, go.Densitymap, go.Densitymapbox] + else "color" + ) range_color = args["range_color"] or [None, None] colorscale_validator = ColorscaleValidator("colorscale", "make_figure") diff --git a/packages/python/plotly/plotly/express/_doc.py b/packages/python/plotly/plotly/express/_doc.py index ebab116b42e..185b33315de 100644 --- a/packages/python/plotly/plotly/express/_doc.py +++ b/packages/python/plotly/plotly/express/_doc.py @@ -531,6 +531,13 @@ "Dict keys are `'lat'` and `'lon'`", "Sets the center point of the map.", ], + map_style=[ + "str (default `'basic'`, needs Map API token)", + "Identifier of base map style, some of which require a Map or Stadia Maps API token to be set using `plotly.express.set_map_access_token()`.", + "Allowed values which do not require a token are `'open-street-map'`, `'white-bg'`, `'carto-positron'`, `'carto-darkmatter'`.", + "Allowed values which require a Map API token are `'basic'`, `'streets'`, `'outdoors'`, `'light'`, `'dark'`, `'satellite'`, `'satellite-streets'`.", + "Allowed values which require a Stadia Maps API token are `'stamen-terrain'`, `'stamen-toner'`, `'stamen-watercolor'`.", + ], mapbox_style=[ "str (default `'basic'`, needs Mapbox API token)", "Identifier of base map style, some of which require a Mapbox or Stadia Maps API token to be set using `plotly.express.set_mapbox_access_token()`.", diff --git a/packages/python/plotly/plotly/subplots.py b/packages/python/plotly/plotly/subplots.py index 018325df904..0ee6a654776 100644 --- a/packages/python/plotly/plotly/subplots.py +++ b/packages/python/plotly/plotly/subplots.py @@ -114,6 +114,7 @@ def make_subplots( - 'scene': 3D Cartesian subplot for scatter3d, cone, etc. - 'polar': Polar subplot for scatterpolar, barpolar, etc. - 'ternary': Ternary subplot for scatterternary + - 'map': Map subplot for scattermap - 'mapbox': Mapbox subplot for scattermapbox - 'domain': Subplot type for traces that are individually positioned. pie, parcoords, parcats, etc. diff --git a/packages/python/plotly/templategen/utils/__init__.py b/packages/python/plotly/templategen/utils/__init__.py index 38ac75e0608..812e5652925 100644 --- a/packages/python/plotly/templategen/utils/__init__.py +++ b/packages/python/plotly/templategen/utils/__init__.py @@ -20,6 +20,7 @@ ("scattergl", "marker"), ("scatter3d", "line"), ("scatter3d", "marker"), + ("scattermap", "marker"), ("scattermapbox", "marker"), ("scatterternary", "marker"), ("scattercarpet", "marker"),