Skip to content

Commit

Permalink
Add color schemes for visualizing vector data
Browse files Browse the repository at this point in the history
  • Loading branch information
giswqs committed Nov 13, 2023
1 parent ada89b1 commit 47cf6d6
Show file tree
Hide file tree
Showing 11 changed files with 634 additions and 59 deletions.
3 changes: 3 additions & 0 deletions docs/map_widgets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# map_widgets module

::: leafmap.map_widgets
40 changes: 23 additions & 17 deletions docs/notebooks/84_read_parquet.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,21 @@
]
},
{
"cell_type": "markdown",
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Use DuckDB."
"color_map = {\n",
" \"Freshwater Forested/Shrub Wetland\": (0, 136, 55),\n",
" \"Freshwater Emergent Wetland\": (127, 195, 28),\n",
" \"Freshwater Pond\": (104, 140, 192),\n",
" \"Estuarine and Marine Wetland\": (102, 194, 165),\n",
" \"Riverine\": (1, 144, 191),\n",
" \"Lake\": (19, 0, 124),\n",
" \"Estuarine and Marine Deepwater\": (0, 124, 136),\n",
" \"Other Freshwater Wetland\": (178, 134, 86),\n",
" }"
]
},
{
Expand All @@ -155,21 +166,16 @@
"metadata": {},
"outputs": [],
"source": [
"import duckdb\n",
"import leafmap.deckgl as leafmap\n",
"\n",
"con = duckdb.connect()\n",
"con.install_extension(\"spatial\")\n",
"con.load_extension(\"spatial\")\n",
"\n",
"state = \"DC\" # Change to the US State of your choice\n",
"url = f\"https://data.source.coop/giswqs/nwi/wetlands/{state}_Wetlands.parquet\"\n",
"df = con.sql(f\"SELECT * EXCLUDE geometry, ST_AsText(ST_GeomFromWKB(geometry)) AS geometry FROM '{url}'\").df()\n",
"gdf = leafmap.df_to_gdf(df, src_crs=\"EPSG:5070\", dst_crs=\"EPSG:4326\")\n",
"\n",
"m = leafmap.Map()\n",
"m.add_gdf(gdf)\n",
"m"
"leafmap.view_vector(gdf, color_column='WETLAND_TYPE', color_map=color_map, opacity=0.5)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"leafmap.Legend(title=\"Wetland Type\", legend_dict=color_map)"
]
}
],
Expand Down
40 changes: 23 additions & 17 deletions examples/notebooks/84_read_parquet.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,21 @@
]
},
{
"cell_type": "markdown",
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Use DuckDB."
"color_map = {\n",
" \"Freshwater Forested/Shrub Wetland\": (0, 136, 55),\n",
" \"Freshwater Emergent Wetland\": (127, 195, 28),\n",
" \"Freshwater Pond\": (104, 140, 192),\n",
" \"Estuarine and Marine Wetland\": (102, 194, 165),\n",
" \"Riverine\": (1, 144, 191),\n",
" \"Lake\": (19, 0, 124),\n",
" \"Estuarine and Marine Deepwater\": (0, 124, 136),\n",
" \"Other Freshwater Wetland\": (178, 134, 86),\n",
" }"
]
},
{
Expand All @@ -155,21 +166,16 @@
"metadata": {},
"outputs": [],
"source": [
"import duckdb\n",
"import leafmap.deckgl as leafmap\n",
"\n",
"con = duckdb.connect()\n",
"con.install_extension(\"spatial\")\n",
"con.load_extension(\"spatial\")\n",
"\n",
"state = \"DC\" # Change to the US State of your choice\n",
"url = f\"https://data.source.coop/giswqs/nwi/wetlands/{state}_Wetlands.parquet\"\n",
"df = con.sql(f\"SELECT * EXCLUDE geometry, ST_AsText(ST_GeomFromWKB(geometry)) AS geometry FROM '{url}'\").df()\n",
"gdf = leafmap.df_to_gdf(df, src_crs=\"EPSG:5070\", dst_crs=\"EPSG:4326\")\n",
"\n",
"m = leafmap.Map()\n",
"m.add_gdf(gdf)\n",
"m"
"leafmap.view_vector(gdf, color_column='WETLAND_TYPE', color_map=color_map, opacity=0.5)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"leafmap.Legend(title=\"Wetland Type\", legend_dict=color_map)"
]
}
],
Expand Down
53 changes: 45 additions & 8 deletions leafmap/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,60 @@ def _use_folium():
return False


def view_vector(vector, zoom_to_layer=True, pickable=True, open_args={}, **kwargs):
def view_vector(
vector,
zoom_to_layer=True,
pickable=True,
color_column=None,
color_scheme="Quantiles",
color_map=None,
color_k=5,
color_args={},
open_args={},
map_args={},
**kwargs,
):
"""Visualize a vector dataset on the map.
Args:
vector (Union[str, GeoDataFrame]): The file path or URL to the vector data, or a GeoDataFrame.
zoom_to_layer (bool, optional): Flag to zoom to the added layer. Defaults to True.
pickable (bool, optional): Flag to enable picking on the added layer. Defaults to True.
open_args (dict, optional): Additional keyword arguments that will be passed to gpd.read_file() if vector is a file path or URL. Defaults to {}.
**kwargs: Additional keyword arguments that will be passed to the vector layer.
vector (Union[str, GeoDataFrame]): The file path or URL to the vector data, or a GeoDataFrame.
zoom_to_layer (bool, optional): Flag to zoom to the added layer. Defaults to True.
pickable (bool, optional): Flag to enable picking on the added layer. Defaults to True.
color_column (Optional[str], optional): The column to be used for color encoding. Defaults to None.
color_map (Optional[Union[str, Dict]], optional): The color map to use for color encoding. It can be a string or a dictionary. Defaults to None.
color_scheme (Optional[str], optional): The color scheme to use for color encoding. Defaults to "Quantiles".
Name of a choropleth classification scheme (requires mapclassify).
A mapclassify.MapClassifier object will be used
under the hood. Supported are all schemes provided by mapclassify (e.g.
'BoxPlot', 'EqualInterval', 'FisherJenks', 'FisherJenksSampled',
'HeadTailBreaks', 'JenksCaspall', 'JenksCaspallForced',
'JenksCaspallSampled', 'MaxP', 'MaximumBreaks',
'NaturalBreaks', 'Quantiles', 'Percentiles', 'StdMean',
'UserDefined'). Arguments can be passed in classification_kwds.
color_k (Optional[int], optional): The number of classes to use for color encoding. Defaults to 5.
color_args (dict, optional): Additional keyword arguments that will be passed to assign_continuous_colors(). Defaults to {}.
open_args (dict, optional): Additional keyword arguments that will be passed to geopandas.read_file(). Defaults to {}.
map_args (dict, optional): Additional keyword arguments that will be passed to lonboard.Map. Defaults to {}.
**kwargs: Additional keyword arguments that will be passed to lonboard.Layer.from_geopandas()
Returns:
None
"""
from .deckgl import Map

m = Map()
m.add_vector(vector, zoom_to_layer, pickable, open_args, **kwargs)
m = Map(**map_args)
m.add_vector(
vector,
zoom_to_layer,
pickable,
color_column,
color_scheme,
color_map,
color_k,
color_args,
open_args,
**kwargs,
)
return m


Expand Down
83 changes: 76 additions & 7 deletions leafmap/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,15 +468,17 @@ def check_color(in_color: Union[str, Tuple]) -> str:
"""Checks the input color and returns the corresponding hex color code.
Args:
in_color (str or tuple): It can be a string (e.g., 'red', '#ffff00', 'ffff00', 'ff0') or RGB tuple (e.g., (255, 127, 0)).
in_color (str or tuple or list): It can be a string (e.g., 'red', '#ffff00', 'ffff00', 'ff0') or RGB tuple (e.g., (255, 127, 0)).
Returns:
str: A hex color code.
"""
import colour

out_color = "#000000" # default black color
if isinstance(in_color, tuple) and len(in_color) == 3:
if (isinstance(in_color, tuple) or isinstance(in_color, list)) and len(
in_color
) == 3:
# rescale color if necessary
if all(isinstance(item, int) for item in in_color):
in_color = [c / 255.0 for c in in_color]
Expand Down Expand Up @@ -4918,7 +4920,11 @@ def classify(
"mapclassify is required for this function. Install with `pip install mapclassify`."
)

if isinstance(data, gpd.GeoDataFrame) or isinstance(data, pd.DataFrame):
if (
isinstance(data, gpd.GeoDataFrame)
or isinstance(data, pd.DataFrame)
or isinstance(data, pd.Series)
):
df = data
else:
try:
Expand Down Expand Up @@ -11756,9 +11762,7 @@ def vector_to_parquet(
gdf.to_parquet(output, **kwargs)


def df_to_gdf(
df, geometry="geometry", src_crs="EPSG:4326", dst_crs=None, **kwargs
):
def df_to_gdf(df, geometry="geometry", src_crs="EPSG:4326", dst_crs=None, **kwargs):
"""
Converts a pandas DataFrame to a GeoPandas GeoDataFrame.
Expand All @@ -11779,7 +11783,7 @@ def df_to_gdf(

# Convert the pandas DataFrame to a GeoPandas GeoDataFrame
gdf = gpd.GeoDataFrame(df, geometry=geometry, crs=src_crs, **kwargs)
if dst_crs is not None and dst_crs != src_crs:
if dst_crs is not None and dst_crs != src_crs:
gdf = gdf.to_crs(dst_crs)

return gdf
Expand Down Expand Up @@ -11911,3 +11915,68 @@ def read_parquet(

con.close()
return result


def assign_discrete_colors(df, column, cmap, to_rgb=True, return_type="array"):
"""
Assigns unique colors to each category in a categorical column of a dataframe.
Args:
df (pandas.DataFrame): The input dataframe.
column (str): The name of the categorical column.
cmap (dict): A dictionary mapping categories to colors.
to_rgb (bool): Whether to convert the colors to RGB values. Defaults to True.
return_type (str): The type of the returned values. Can be 'list' or 'array'. Defaults to 'array'.
Returns:
list: A list of colors for each category in the categorical column.
"""
import numpy as np

# Copy the categorical column from the original dataframe
category_column = df[column].copy()

# Map colors to the categorical values
category_column = category_column.map(cmap)

values = category_column.values.tolist()

if to_rgb:
values = [hex_to_rgb(check_color(color)) for color in values]
if return_type == "array":
values = np.array(values, dtype=np.uint8)

return values


def assign_continuous_colors(
df,
column,
cmap=None,
colors=None,
labels=None,
scheme="Quantiles",
k=5,
legend_kwds=None,
classification_kwds=None,
to_rgb=True,
return_type="array",
return_legend=False,
):
import numpy as np

data = df[[column]].copy()
new_df, legend = classify(
data, column, cmap, colors, labels, scheme, k, legend_kwds, classification_kwds
)
values = new_df["color"].values.tolist()

if to_rgb:
values = [hex_to_rgb(check_color(color)) for color in values]
if return_type == "array":
values = np.array(values, dtype=np.uint8)

if return_legend:
return values, legend
else:
return values
Loading

0 comments on commit 47cf6d6

Please sign in to comment.