Skip to content

BassMap module

Main module.

Mapomatic (Map)

Source code in bassmap/bassmap.py
class Mapomatic(Map):

    def __init__(self, center=[20,0], **kwargs) -> None:
        if "scroll_wheel_zoom" not in kwargs:
            kwargs["scroll_wheel_zoom"] = True

        super().__init__(center = center, **kwargs)

    def add_marker(self, marker):
        self.markers.append(marker)
        self.add_layer(marker)

    def add_image(self, url, position, size, opacity=1.0, layer_name=None):
        image = ImageOverlay(
            url=url,
            bounds=[position, (position[0] + size[0], position[1] + size[1])],
            opacity=opacity,
            name=layer_name
        )
        self.add_layer(image)

    def add_raster(self, url, name='Raster', fit_bounds=True, **kwargs):
        """Adds a raster layer to the map.
        Args:
            url (str): The URL of the raster layer.
            name (str, optional): The name of the raster layer. Defaults to 'Raster'.
            fit_bounds (bool, optional): Whether to fit the map bounds to the raster layer. Defaults to True.
        """
        import httpx

        titiler_endpoint = "https://titiler.xyz"

        r = httpx.get(
            f"{titiler_endpoint}/cog/info",
            params = {
                "url": url,
            }
        ).json()

        bounds = r["bounds"]

        r = httpx.get(
            f"{titiler_endpoint}/cog/tilejson.json",
            params = {
                "url": url,
            }
        ).json()

        tile = r["tiles"][0]

        self.add_tile_layer(url=tile, name=name, **kwargs)

        if fit_bounds:
            bbox = [[bounds[1], bounds[0]], [bounds[3], bounds[2]]]
            self.fit_bounds(bbox)

    def add_tile_layer(self, url, name, attribution = "", **kwargs):
        """Adds a tile layer to the map.

        Args:
            url (str): The URL of the tile layer.
            name (str): The name of the tile layer
            attribution (str, optional): The attribution of the tile layer. Defaults to **
            """
        tile_layer = ipyleaflet.TileLayer(
            url = url,
            name = name,
            attribution = attribution,
            **kwargs
        )
        self.add_layer(tile_layer)

    def add_layers_control(self, position="topright", **kwargs):
        """Adds a layers control to the map.

        Args:
            kwargs: Keyword arguments to pass to the layers control
        """
        layers_control = ipyleaflet.LayersControl(position = position, **kwargs)
        self.add_control(layers_control)

    def add_fullscreen_control(self, position="topleft"):
        """Adds a fullscreen control to the map.

        Args:
            kwargs: Keyward arguments to pass to the layers control.
        """
        fullscreen_control = ipyleaflet.FullScreenControl(position=position)
        self.add_control(fullscreen_control)

    def add_basemap(self, basemap_name, url_template):
        """
        Adds a basemap to the map using a URL template.

        Parameters:
        basemap_name (str): The name of the basemap to add.
        url_template (str): The URL template to use for the new basemap layer. Must be 
            a valid XYZ tile service.
        """
        # Remove the default OpenStreetMap basemap layer, if present
        if len(self.layers) > 1:
            self.remove_layer(self.layers[0])

        # Add the new basemap layer
        new_layer = TileLayer(url=url_template, attribution=basemap_name)
        self.add_layer(new_layer)

    def add_shp(self, shp_path):
        """
        Adds a shapefile to the map. 

        Parameters:
        shp_path (str): The file path or HTTP URL to the shapefile in a zip file.
        """
        # If the path is an HTTP URL, download and unzip the shapefile
        if shp_path.startswith('http'):
            response = requests.get(shp_path)
            with zipfile.ZipFile(io.BytesIO(response.content)) as zip_ref:
                zip_ref.extractall()
            shp_path = shp_path.split('/')[-1].split('.')[0] + '.shp'

        # Read the shapefile using GeoPandas
        gdf = gpd.read_file(shp_path)

        # Convert the GeoDataFrame to a GeoJSON object
        geojson = GeoJSON(data=gdf.__geo_interface__)

        # Add the GeoJSON layer to the map
        self.add_layer(geojson)

        # Add a layer control to the map
        control = LayersControl(position='topright')
        self.add_control(control)

    def add_geojson(self, geojson_path):
        """
        Adds a GeoJSON file to the map. 

        Parameters:
        geojson_path (str or dict): The file path or dictionary object containing GeoJSON data.
        """
        # If the path is an HTTP URL, download the GeoJSON file
        if isinstance(geojson_path, str) and geojson_path.startswith('http'):
            response = requests.get(geojson_path)
            geojson_data = response.json()
        # Otherwise, assume it's a file path or a dictionary object
        else:
            with open(geojson_path) as f:
                geojson_data = json.load(f)

        # Create a GeoJSON layer and add it to the map
        geojson = GeoJSON(data=geojson_data)
        self.add_layer(geojson)

        # Add a layer control to the map
        control = LayersControl(position='topright')
        self.add_control(control)

    def add_vector(self, vector_data):
        """
        Adds a vector data to the map. The vector data can be in any GeoPandas-supported
        format, such as GeoJSON, shapefile, GeoDataFrame, etc.

        Parameters:
        vector_data (str or dict or GeoDataFrame): The vector data to add to the map. 
            Can be a file path or URL to the vector data, a dictionary object containing 
            GeoJSON data, or a GeoDataFrame.
        """
        # If the vector data is a file path or URL, read it using GeoPandas
        if isinstance(vector_data, str):
            try:
                gdf = gpd.read_file(vector_data)
            except ValueError:
                gdf = gpd.read_file(vector_data, encoding='utf-8')
        # If the vector data is a dictionary object, create a GeoDataFrame
        elif isinstance(vector_data, dict):
            gdf = gpd.GeoDataFrame.from_features(vector_data['features'])
        # If the vector data is already a GeoDataFrame, use it directly
        elif isinstance(vector_data, gpd.GeoDataFrame):
            gdf = vector_data
        else:
            raise ValueError('Invalid vector data format. Must be a file path or URL, a dictionary object containing GeoJSON data, or a GeoDataFrame.')

        # Convert the GeoDataFrame to a GeoJSON object
        geojson = GeoJSON(data=gdf.__geo_interface__)

        # Add the GeoJSON layer to the map
        self.add_layer(geojson)

        # Add a layer control to the map
        control = LayersControl(position='topright')
        self.add_control(control)

    def select_basemap(self, **kwargs):
        """
        Adds a basemap selector to the map instance.

        Parameters:
            self: bassmap Mapomatic: map instance called by user

        Returns:
            bassmap Mapomatic: displays drop down menu when called to the Mapomatic class
        """

        output_widget = widgets.Output(layout = {'border': '1px solid white'})
        output_widget.clear_output()

        with output_widget:
            display(HTML('''
                <style>
                    .widget-dropdown { background-color: black !important; }
                    .widget-dropdown .widget-label { color: olive !important; }
                </style>
            '''))

        basemap_ctrl = WidgetControl(widget = output_widget, position='topright')
        self.add_control(basemap_ctrl)

        dropdown = widgets.Dropdown(
            options = ["OpenStreetMap", "ESRI Imagery", "OpenTopoMap", "NatGeo World Map", "Light Canvas"], 
            value = None,
            description = 'Basemap',
            style = {'description_width': 'initial', 'button_width': '100px', 'button_color': 'olive', 'description_color': 'olive', 'background-color': 'olive'}
        )

        icon_html = '<i class="fa fa-window-close" aria-hidden="true"></i>'
        close_button = widgets.ToggleButton(
            value = True,
            tooltip = "Toggle basemap selector",
            description = 'Close',
            icon = icon_html,
            button_style = "primary",
        )

        close_button.style.button_color = "white"
        close_button.style.font_weight = "bold"

        close_button_icon = HTML(
            '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">'
            '<i class="fa fa-times"></i>'
        )

        widget_menu = widgets.VBox([close_button, dropdown])


        with output_widget:
            display(widget_menu)

        def change_basemap(select):
            if select["new"] == "OpenStreetMap":
                self.add_basemap(basemap_name= "OpenStreetMap", url_template="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png")

            if select["new"] == "ESRI Imagery":
                self.add_basemap(basemap_name= "Esri.WorldImagery", url_template="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}")

            if select["new"] == "OpenTopoMap":
                self.add_basemap(basemap_name= "OpenTopoMap", url_template="https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png")

            if select["new"] == "NatGeo World Map":
                self.add_basemap(basemap_name= "Esri.NatGeoWorldMap", url_template="https://server.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}")

            if select["new"] == "Light Canvas":
                self.add_basemap(basemap_name= "CartoDB.Positron", url_template="https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png")

        dropdown.observe(change_basemap, "value")

        def close_basemap(select):

            if select["new"] == True:
                output_widget.clear_output()
                with output_widget:
                    display(widget_menu)

            else:
                output_widget.clear_output()
                with output_widget:
                    display(close_button)

        close_button.observe(close_basemap, "value")

add_basemap(self, basemap_name, url_template)

Adds a basemap to the map using a URL template.

basemap_name (str): The name of the basemap to add. url_template (str): The URL template to use for the new basemap layer. Must be a valid XYZ tile service.

Source code in bassmap/bassmap.py
def add_basemap(self, basemap_name, url_template):
    """
    Adds a basemap to the map using a URL template.

    Parameters:
    basemap_name (str): The name of the basemap to add.
    url_template (str): The URL template to use for the new basemap layer. Must be 
        a valid XYZ tile service.
    """
    # Remove the default OpenStreetMap basemap layer, if present
    if len(self.layers) > 1:
        self.remove_layer(self.layers[0])

    # Add the new basemap layer
    new_layer = TileLayer(url=url_template, attribution=basemap_name)
    self.add_layer(new_layer)

add_fullscreen_control(self, position='topleft')

Adds a fullscreen control to the map.

Parameters:

Name Type Description Default
kwargs

Keyward arguments to pass to the layers control.

required
Source code in bassmap/bassmap.py
def add_fullscreen_control(self, position="topleft"):
    """Adds a fullscreen control to the map.

    Args:
        kwargs: Keyward arguments to pass to the layers control.
    """
    fullscreen_control = ipyleaflet.FullScreenControl(position=position)
    self.add_control(fullscreen_control)

add_geojson(self, geojson_path)

Adds a GeoJSON file to the map.

geojson_path (str or dict): The file path or dictionary object containing GeoJSON data.

Source code in bassmap/bassmap.py
def add_geojson(self, geojson_path):
    """
    Adds a GeoJSON file to the map. 

    Parameters:
    geojson_path (str or dict): The file path or dictionary object containing GeoJSON data.
    """
    # If the path is an HTTP URL, download the GeoJSON file
    if isinstance(geojson_path, str) and geojson_path.startswith('http'):
        response = requests.get(geojson_path)
        geojson_data = response.json()
    # Otherwise, assume it's a file path or a dictionary object
    else:
        with open(geojson_path) as f:
            geojson_data = json.load(f)

    # Create a GeoJSON layer and add it to the map
    geojson = GeoJSON(data=geojson_data)
    self.add_layer(geojson)

    # Add a layer control to the map
    control = LayersControl(position='topright')
    self.add_control(control)

add_layers_control(self, position='topright', **kwargs)

Adds a layers control to the map.

Parameters:

Name Type Description Default
kwargs

Keyword arguments to pass to the layers control

{}
Source code in bassmap/bassmap.py
def add_layers_control(self, position="topright", **kwargs):
    """Adds a layers control to the map.

    Args:
        kwargs: Keyword arguments to pass to the layers control
    """
    layers_control = ipyleaflet.LayersControl(position = position, **kwargs)
    self.add_control(layers_control)

add_raster(self, url, name='Raster', fit_bounds=True, **kwargs)

Adds a raster layer to the map.

Parameters:

Name Type Description Default
url str

The URL of the raster layer.

required
name str

The name of the raster layer. Defaults to 'Raster'.

'Raster'
fit_bounds bool

Whether to fit the map bounds to the raster layer. Defaults to True.

True
Source code in bassmap/bassmap.py
def add_raster(self, url, name='Raster', fit_bounds=True, **kwargs):
    """Adds a raster layer to the map.
    Args:
        url (str): The URL of the raster layer.
        name (str, optional): The name of the raster layer. Defaults to 'Raster'.
        fit_bounds (bool, optional): Whether to fit the map bounds to the raster layer. Defaults to True.
    """
    import httpx

    titiler_endpoint = "https://titiler.xyz"

    r = httpx.get(
        f"{titiler_endpoint}/cog/info",
        params = {
            "url": url,
        }
    ).json()

    bounds = r["bounds"]

    r = httpx.get(
        f"{titiler_endpoint}/cog/tilejson.json",
        params = {
            "url": url,
        }
    ).json()

    tile = r["tiles"][0]

    self.add_tile_layer(url=tile, name=name, **kwargs)

    if fit_bounds:
        bbox = [[bounds[1], bounds[0]], [bounds[3], bounds[2]]]
        self.fit_bounds(bbox)

add_shp(self, shp_path)

Adds a shapefile to the map.

shp_path (str): The file path or HTTP URL to the shapefile in a zip file.

Source code in bassmap/bassmap.py
def add_shp(self, shp_path):
    """
    Adds a shapefile to the map. 

    Parameters:
    shp_path (str): The file path or HTTP URL to the shapefile in a zip file.
    """
    # If the path is an HTTP URL, download and unzip the shapefile
    if shp_path.startswith('http'):
        response = requests.get(shp_path)
        with zipfile.ZipFile(io.BytesIO(response.content)) as zip_ref:
            zip_ref.extractall()
        shp_path = shp_path.split('/')[-1].split('.')[0] + '.shp'

    # Read the shapefile using GeoPandas
    gdf = gpd.read_file(shp_path)

    # Convert the GeoDataFrame to a GeoJSON object
    geojson = GeoJSON(data=gdf.__geo_interface__)

    # Add the GeoJSON layer to the map
    self.add_layer(geojson)

    # Add a layer control to the map
    control = LayersControl(position='topright')
    self.add_control(control)

add_tile_layer(self, url, name, attribution='', **kwargs)

Adds a tile layer to the map.

Parameters:

Name Type Description Default
url str

The URL of the tile layer.

required
name str

The name of the tile layer

required
attribution str

The attribution of the tile layer. Defaults to **

''
Source code in bassmap/bassmap.py
def add_tile_layer(self, url, name, attribution = "", **kwargs):
    """Adds a tile layer to the map.

    Args:
        url (str): The URL of the tile layer.
        name (str): The name of the tile layer
        attribution (str, optional): The attribution of the tile layer. Defaults to **
        """
    tile_layer = ipyleaflet.TileLayer(
        url = url,
        name = name,
        attribution = attribution,
        **kwargs
    )
    self.add_layer(tile_layer)

add_vector(self, vector_data)

Adds a vector data to the map. The vector data can be in any GeoPandas-supported format, such as GeoJSON, shapefile, GeoDataFrame, etc.

vector_data (str or dict or GeoDataFrame): The vector data to add to the map. Can be a file path or URL to the vector data, a dictionary object containing GeoJSON data, or a GeoDataFrame.

Source code in bassmap/bassmap.py
def add_vector(self, vector_data):
    """
    Adds a vector data to the map. The vector data can be in any GeoPandas-supported
    format, such as GeoJSON, shapefile, GeoDataFrame, etc.

    Parameters:
    vector_data (str or dict or GeoDataFrame): The vector data to add to the map. 
        Can be a file path or URL to the vector data, a dictionary object containing 
        GeoJSON data, or a GeoDataFrame.
    """
    # If the vector data is a file path or URL, read it using GeoPandas
    if isinstance(vector_data, str):
        try:
            gdf = gpd.read_file(vector_data)
        except ValueError:
            gdf = gpd.read_file(vector_data, encoding='utf-8')
    # If the vector data is a dictionary object, create a GeoDataFrame
    elif isinstance(vector_data, dict):
        gdf = gpd.GeoDataFrame.from_features(vector_data['features'])
    # If the vector data is already a GeoDataFrame, use it directly
    elif isinstance(vector_data, gpd.GeoDataFrame):
        gdf = vector_data
    else:
        raise ValueError('Invalid vector data format. Must be a file path or URL, a dictionary object containing GeoJSON data, or a GeoDataFrame.')

    # Convert the GeoDataFrame to a GeoJSON object
    geojson = GeoJSON(data=gdf.__geo_interface__)

    # Add the GeoJSON layer to the map
    self.add_layer(geojson)

    # Add a layer control to the map
    control = LayersControl(position='topright')
    self.add_control(control)

select_basemap(self, **kwargs)

Adds a basemap selector to the map instance.

Parameters:

Name Type Description Default
self

bassmap Mapomatic: map instance called by user

required

Returns:

Type Description
bassmap Mapomatic

displays drop down menu when called to the Mapomatic class

Source code in bassmap/bassmap.py
def select_basemap(self, **kwargs):
    """
    Adds a basemap selector to the map instance.

    Parameters:
        self: bassmap Mapomatic: map instance called by user

    Returns:
        bassmap Mapomatic: displays drop down menu when called to the Mapomatic class
    """

    output_widget = widgets.Output(layout = {'border': '1px solid white'})
    output_widget.clear_output()

    with output_widget:
        display(HTML('''
            <style>
                .widget-dropdown { background-color: black !important; }
                .widget-dropdown .widget-label { color: olive !important; }
            </style>
        '''))

    basemap_ctrl = WidgetControl(widget = output_widget, position='topright')
    self.add_control(basemap_ctrl)

    dropdown = widgets.Dropdown(
        options = ["OpenStreetMap", "ESRI Imagery", "OpenTopoMap", "NatGeo World Map", "Light Canvas"], 
        value = None,
        description = 'Basemap',
        style = {'description_width': 'initial', 'button_width': '100px', 'button_color': 'olive', 'description_color': 'olive', 'background-color': 'olive'}
    )

    icon_html = '<i class="fa fa-window-close" aria-hidden="true"></i>'
    close_button = widgets.ToggleButton(
        value = True,
        tooltip = "Toggle basemap selector",
        description = 'Close',
        icon = icon_html,
        button_style = "primary",
    )

    close_button.style.button_color = "white"
    close_button.style.font_weight = "bold"

    close_button_icon = HTML(
        '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">'
        '<i class="fa fa-times"></i>'
    )

    widget_menu = widgets.VBox([close_button, dropdown])


    with output_widget:
        display(widget_menu)

    def change_basemap(select):
        if select["new"] == "OpenStreetMap":
            self.add_basemap(basemap_name= "OpenStreetMap", url_template="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png")

        if select["new"] == "ESRI Imagery":
            self.add_basemap(basemap_name= "Esri.WorldImagery", url_template="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}")

        if select["new"] == "OpenTopoMap":
            self.add_basemap(basemap_name= "OpenTopoMap", url_template="https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png")

        if select["new"] == "NatGeo World Map":
            self.add_basemap(basemap_name= "Esri.NatGeoWorldMap", url_template="https://server.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}")

        if select["new"] == "Light Canvas":
            self.add_basemap(basemap_name= "CartoDB.Positron", url_template="https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png")

    dropdown.observe(change_basemap, "value")

    def close_basemap(select):

        if select["new"] == True:
            output_widget.clear_output()
            with output_widget:
                display(widget_menu)

        else:
            output_widget.clear_output()
            with output_widget:
                display(close_button)

    close_button.observe(close_basemap, "value")

generate_input_points()

Input name and either generate random point or input coordinates to shapefile and display on map.

Parameters:

Name Type Description Default
name

Name of the location.

required
lat int

The latitude value

required
lon int

The longitude value

required
generate_random int

Whether to generate random coordinates or use custom

required

Exceptions:

Type Description
ValueError

Latitude must be between -90 and 90 degrees

ValueError

Longitude must be between -180 and 180 degrees

Source code in bassmap/bassmap.py
def generate_input_points():
    """Input name and either generate random point or input coordinates to shapefile and display on map.

    Args:
        name (): Name of the location.
        lat (int, optional): The latitude value
        lon (int, optional): The longitude value
        generate_random (int, optional): Whether to generate random coordinates or use custom
    Raises:
        ValueError: Latitude must be between -90 and 90 degrees
        ValueError: Longitude must be between -180 and 180 degrees
    """

    while True:
        name = input("Enter location name (or 'q' to finish): ")

        if name == 'q':
            break

        generate_random = input("Generate random point? (y/n): ")
        if generate_random.lower() == "y":

            lat = random.uniform(-90, 90)
            lon = random.uniform(-180, 180)
            locations[name] = {'lat': lat, 'lon': lon}
            print(f"The location {name} is located at ({lat}, {lon}).\n")
        else:
            lat = input("Enter latitude: ")
            lon = input("Enter longitude: ")

            try:
                lat = float(lat)
                lon = float(lon)

                if lat < -90 or lat > 90:
                    raise ValueError("Latitude must be between -90 and 90 degrees")
                if lon < -180 or lon > 180:
                    raise ValueError("Longitude must be between -180 and 180 degrees")

                locations[name] = {'lat': lat, 'lon': lon}

                print(f"The location {name} is located at ({lat}, {lon}).\n")

            except ValueError as e:
                print(f"Invalid input: {e}")

get_NDVI(red_band, nir_band, georef)

Calculates the Normalized Difference Vegetation Index (NDVI) from Red and Near Infrared (NIR) bands.

Parameters:

Name Type Description Default
red_band numpy.ndarray

A 2D or 3D array containing the Red band data.

required
nir_band numpy.ndarray

A 2D or 3D array containing the Near Infrared band data.

required
georef osgeo.gdal.Dataset

A GDAL dataset containing georeferencing information for the input bands.

required

Returns:

Type Description
str

The path to the created NDVI file.

Exceptions:

Type Description
ValueError

If the input arrays are not of the same shape.

ValueError

If the input arrays have different data types.

ValueError

If the input arrays have invalid values.

ImportError

If the GDAL library is not installed.

Examples:

red_band = gdal.Open('path/to/red_band.tif').ReadAsArray() nir_band = gdal.Open('path/to/nir_band.tif').ReadAsArray() georef = gdal.Open('path/to/reference.tif')

Generate an NDVI composite image and download it

ndvi_path = get_NDVI(red_band, nir_band, georef)

Download the file at ndvi_path

Source code in bassmap/bassmap.py
def get_NDVI(red_band, nir_band, georef):
    """
    Calculates the Normalized Difference Vegetation Index (NDVI) from Red and Near Infrared (NIR) bands.

    Args:
        red_band (numpy.ndarray): A 2D or 3D array containing the Red band data.
        nir_band (numpy.ndarray): A 2D or 3D array containing the Near Infrared band data.
        georef (osgeo.gdal.Dataset): A GDAL dataset containing georeferencing information for the input bands.

    Returns:
        str: The path to the created NDVI file.

    Raises:
        ValueError: If the input arrays are not of the same shape.
        ValueError: If the input arrays have different data types.
        ValueError: If the input arrays have invalid values.
        ImportError: If the GDAL library is not installed.

    Examples:
        red_band = gdal.Open('path/to/red_band.tif').ReadAsArray()
        nir_band = gdal.Open('path/to/nir_band.tif').ReadAsArray()
        georef = gdal.Open('path/to/reference.tif')

        # Generate an NDVI composite image and download it
        ndvi_path = get_NDVI(red_band, nir_band, georef)
        # Download the file at ndvi_path
    """

    from osgeo import gdal

    # Scale the input bands to the range of 0-255
    red_band = (red_band / 65535.0) * 255.0
    nir_band = (nir_band / 65535.0) * 255.0

    # Calculate the NDVI from the NIR and Red bands
    ndvi = np.empty_like(nir_band, dtype=np.float32)
    ndvi.fill(np.nan)
    valid = np.logical_and(red_band != 0, nir_band != 0)
    ndvi[valid] = (nir_band[valid] - red_band[valid]) / (nir_band[valid] + red_band[valid])

    # Get the dimensions and georeferencing information from one of the input files
    xsize = georef.RasterXSize
    ysize = georef.RasterYSize
    proj = georef.GetProjection()
    geotrans = georef.GetGeoTransform()
    driver = gdal.GetDriverByName('GTiff')

    # Create a new TIFF file and write the NDVI array to it
    ndvi_path = os.path.join(tmp_dir.name, 'ndvi_composite.tif')
    ndvi_ds = driver.Create(ndvi_path, xsize, ysize, 1, gdal.GDT_Float32)

    if ndvi_ds is not None:
        ndvi_ds.SetProjection(proj)
        ndvi_ds.SetGeoTransform(geotrans)
        ndvi_ds.GetRasterBand(1).WriteArray(ndvi)
        ndvi_ds.FlushCache()
    else:
        raise Exception("Failed to create TIFF file")

    return ndvi_path

get_color_infrared(nir_band, red_band, green_band, georef)

Combines a near-infrared band, red band, and green band into a single 3-band image, where the near-infrared band is displayed as red, the red band is displayed as green, and the green band is displayed as blue. This produces a color infrared image that accentuates the buildings in the scene.

Parameters:

Name Type Description Default
nir_band numpy array

A 2D numpy array representing a near-infrared band

required
red_band numpy array

A 2D numpy array representing a red band

required
green_band numpy array

A 2D numpy array representing a green band

required
georef osgeo.gdal.Dataset

A GDAL dataset containing georeferencing information for the input bands.

required

Returns:

Type Description
str

The path to the created color infrared file.

Exceptions:

Type Description
ValueError

If the input arrays are not of the same shape.

ValueError

If the input arrays have different data types.

ValueError

If the input arrays have invalid values.

ImportError

If the GDAL library is not installed.

Examples:

Load the input bands as numpy arrays

nir_band = gdal.Open('path/to/nir_band.tif').ReadAsArray() red_band = gdal.Open('path/to/red_band.tif').ReadAsArray() green_band = gdal.Open('path/to/green_band.tif').ReadAsArray() georef = gdal.Open('path/to/reference.tif')

Generate a color infrared composite image and download it

color_infrared_path = get_color_infrared(nir_band, red_band, green_band, georef)

Download the file at color_infrared_path

Source code in bassmap/bassmap.py
def get_color_infrared(nir_band, red_band, green_band, georef):

    """
    Combines a near-infrared band, red band, and green band into a single 3-band image,
    where the near-infrared band is displayed as red, the red band is displayed as green,
    and the green band is displayed as blue. This produces a color infrared image that
    accentuates the buildings in the scene.

    Args:
        nir_band (numpy array): A 2D numpy array representing a near-infrared band
        red_band (numpy array): A 2D numpy array representing a red band
        green_band (numpy array): A 2D numpy array representing a green band
        georef (osgeo.gdal.Dataset): A GDAL dataset containing georeferencing information for the input bands.

    Returns:
        str: The path to the created color infrared file.

    Raises:
        ValueError: If the input arrays are not of the same shape.
        ValueError: If the input arrays have different data types.
        ValueError: If the input arrays have invalid values.
        ImportError: If the GDAL library is not installed.

    Example:
        # Load the input bands as numpy arrays
        nir_band = gdal.Open('path/to/nir_band.tif').ReadAsArray()
        red_band = gdal.Open('path/to/red_band.tif').ReadAsArray()
        green_band = gdal.Open('path/to/green_band.tif').ReadAsArray()
        georef = gdal.Open('path/to/reference.tif')

        # Generate a color infrared composite image and download it
        color_infrared_path = get_color_infrared(nir_band, red_band, green_band, georef)
        # Download the file at color_infrared_path
    """

    from osgeo import gdal

    # Combine the three bands into a single 3D array with 3 bands
    color_infrared = np.array([nir_band, red_band, green_band], dtype=np.uint16)

    # Get the dimensions and georeferencing information from one of the input files
    xsize = georef.RasterXSize
    ysize = georef.RasterYSize
    proj = georef.GetProjection()
    geotrans = georef.GetGeoTransform()
    driver = gdal.GetDriverByName('GTiff')

    # Create a new TIFF file and write the NDVI array to it
    color_infrared_path = os.path.join(tmp_dir.name, 'color_infrared_comp.tif')
    color_infrared_ds = driver.Create(color_infrared_path, xsize, ysize, 3, gdal.GDT_Float32)

    if color_infrared_ds is not None:
        color_infrared_ds.SetProjection(proj)
        color_infrared_ds.SetGeoTransform(geotrans)
        color_infrared_ds.GetRasterBand(1).WriteArray(color_infrared[0])
        color_infrared_ds.GetRasterBand(2).WriteArray(color_infrared[1])
        color_infrared_ds.GetRasterBand(3).WriteArray(color_infrared[2])
        color_infrared_ds.FlushCache()

    else:
        raise Exception("Failed to create TIFF file")

    return color_infrared_path

get_false_color(swir2_band, swir_band, red_band, georef)

Combines a short wave infrared 2 band, a short wave infrared band, and red band into a single 3-band image, where the a short wave infrared 2 band is displayed as red, the a short wave infrared band is displayed as green, and the red band is displayed as blue. This produces a false-color image that accentuates the vegetation in the scene.

Parameters:

Name Type Description Default
swir2_band numpy array

A 2D numpy array representing a short wave infrared 2 band

required
swir_band numpy array

A 2D numpy array representing a short wave infrared band

required
red_band numpy array

A 2D numpy array representing a red band

required
georef osgeo.gdal.Dataset

A GDAL dataset containing georeferencing information for the input bands.

required

Returns:

Type Description
str

The path to the created false-color file.

Exceptions:

Type Description
ValueError

If the input arrays are not of the same shape.

ValueError

If the input arrays have different data types.

ValueError

If the input arrays have invalid values.

ImportError

If the GDAL library is not installed.

Examples:

Load the input bands as numpy arrays

swir2_band = gdal.Open('path/to/swir2_band.tif').ReadAsArray() swir_band = gdal.Open('path/to/swir_band.tif').ReadAsArray() red_band = gdal.Open('path/to/red_band.tif').ReadAsArray() georef = gdal.Open('path/to/reference.tif')

Generate a false-color composite image and download it

false_color_path = false_color_path(swir2_band, swir_band, red_band, georef)

Download the file at false_color_path

Source code in bassmap/bassmap.py
def get_false_color(swir2_band, swir_band, red_band, georef):

    """
    Combines a short wave infrared 2 band, a short wave infrared band, and red band into a single 3-band image,
    where the a short wave infrared 2 band is displayed as red, the a short wave infrared band is displayed as green,
    and the red band is displayed as blue. This produces a false-color image that
    accentuates the vegetation in the scene.

    Args:
        swir2_band (numpy array): A 2D numpy array representing a short wave infrared 2 band
        swir_band (numpy array): A 2D numpy array representing a short wave infrared band
        red_band (numpy array): A 2D numpy array representing a red band
        georef (osgeo.gdal.Dataset): A GDAL dataset containing georeferencing information for the input bands.

    Returns:
        str: The path to the created false-color file.

    Raises:
        ValueError: If the input arrays are not of the same shape.
        ValueError: If the input arrays have different data types.
        ValueError: If the input arrays have invalid values.
        ImportError: If the GDAL library is not installed.

    Example:
        # Load the input bands as numpy arrays
        swir2_band = gdal.Open('path/to/swir2_band.tif').ReadAsArray()
        swir_band = gdal.Open('path/to/swir_band.tif').ReadAsArray()
        red_band = gdal.Open('path/to/red_band.tif').ReadAsArray()
        georef = gdal.Open('path/to/reference.tif')

        # Generate a false-color composite image and download it
        false_color_path = false_color_path(swir2_band, swir_band, red_band, georef)
        # Download the file at false_color_path
    """

    from osgeo import gdal

    # Combine the three bands into a single 3D array with 3 bands
    false_color = np.array([swir2_band, swir_band, red_band], dtype=np.uint16)

    # Get the dimensions and georeferencing information from one of the input files
    xsize = georef.RasterXSize
    ysize = georef.RasterYSize
    proj = georef.GetProjection()
    geotrans = georef.GetGeoTransform()
    driver = gdal.GetDriverByName('GTiff')

    # Create a new TIFF file and write the NDVI array to it
    false_color_path = os.path.join(tmp_dir.name, 'false_color_comp.tif')
    false_color_ds = driver.Create(false_color_path, xsize, ysize, 3, gdal.GDT_Float32)

    if false_color_ds is not None:
        false_color_ds.SetProjection(proj)
        false_color_ds.SetGeoTransform(geotrans)
        false_color_ds.GetRasterBand(1).WriteArray(false_color[0])
        false_color_ds.GetRasterBand(2).WriteArray(false_color[1])
        false_color_ds.GetRasterBand(3).WriteArray(false_color[2])
        false_color_ds.FlushCache()

    else:
        raise Exception("Failed to create TIFF file")

    return false_color_path

get_health_veg(nir_band, swir_band, blue_band, georef)

Combines a near infrared band, a short wave infrared band, and blue band into a single 3-band image, where the a near infrared band is displayed as red, the a short wave infrared band is displayed as green, and the blue band is displayed as blue. This produces a false-color image that accentuates the healthy vegetation in the scene.

Parameters:

Name Type Description Default
nir_band numpy array

A 2D numpy array representing a near infrared band

required
swir_band numpy array

A 2D numpy array representing a short wave infrared band

required
blue_band numpy array

A 2D numpy array representing a blue band

required
georef osgeo.gdal.Dataset

A GDAL dataset containing georeferencing information for the input bands.

required

Returns:

Type Description
str

The path to the created healthy vegetation file.

Exceptions:

Type Description
ValueError

If the input arrays are not of the same shape.

ValueError

If the input arrays have different data types.

ValueError

If the input arrays have invalid values.

ImportError

If the GDAL library is not installed.

Examples:

Load the input bands as numpy arrays

nir_band = gdal.Open('path/to/nir_band.tif').ReadAsArray() swir_band = gdal.Open('path/to/swir_band.tif').ReadAsArray() blue_band = gdal.Open('path/to/blue_band.tif').ReadAsArray() georef = gdal.Open('path/to/reference.tif')

Generate a healthy vegetation composite image and download it

healthy_veg_path = get_health_veg(nir_band, swir_band, blue_band, georef)

Download the file at healthy_veg_path

Source code in bassmap/bassmap.py
def get_health_veg(nir_band, swir_band, blue_band, georef):

    """
    Combines a near infrared band, a short wave infrared band, and blue band into a single 3-band image,
    where the a near infrared band is displayed as red, the a short wave infrared band is displayed as green,
    and the blue band is displayed as blue. This produces a false-color image that
    accentuates the healthy vegetation in the scene.

    Args:
        nir_band (numpy array): A 2D numpy array representing a near infrared band
        swir_band (numpy array): A 2D numpy array representing a short wave infrared band
        blue_band (numpy array): A 2D numpy array representing a blue band
        georef (osgeo.gdal.Dataset): A GDAL dataset containing georeferencing information for the input bands.

    Returns:
        str: The path to the created healthy vegetation file.

    Raises:
        ValueError: If the input arrays are not of the same shape.
        ValueError: If the input arrays have different data types.
        ValueError: If the input arrays have invalid values.
        ImportError: If the GDAL library is not installed.

    Example:
        # Load the input bands as numpy arrays
        nir_band = gdal.Open('path/to/nir_band.tif').ReadAsArray()
        swir_band = gdal.Open('path/to/swir_band.tif').ReadAsArray()
        blue_band = gdal.Open('path/to/blue_band.tif').ReadAsArray()
        georef = gdal.Open('path/to/reference.tif')

        # Generate a healthy vegetation composite image and download it
        healthy_veg_path = get_health_veg(nir_band, swir_band, blue_band, georef)
        # Download the file at healthy_veg_path
    """

    from osgeo import gdal

    # Combine the three bands into a single 3D array with 3 bands
    healthy_veg = np.array([nir_band, swir_band, blue_band], dtype=np.uint16)

    # Get the dimensions and georeferencing information from one of the input files
    xsize = georef.RasterXSize
    ysize = georef.RasterYSize
    proj = georef.GetProjection()
    geotrans = georef.GetGeoTransform()
    driver = gdal.GetDriverByName('GTiff')

    # Create a new TIFF file and write the composite array to it
    healthy_veg_ds = driver.Create('/tmp/healthy_veg_comp.tif', xsize, ysize, 3, gdal.GDT_UInt16)

    # Create a new TIFF file and write the NDVI array to it
    healthy_veg_path = os.path.join(tmp_dir.name, 'healthy_veg_comp.tif')
    healthy_veg_ds = driver.Create(healthy_veg_path, xsize, ysize, 3, gdal.GDT_Float32)

    if healthy_veg_ds is not None:
        healthy_veg_ds.SetProjection(proj)
        healthy_veg_ds.SetGeoTransform(geotrans)
        healthy_veg_ds.GetRasterBand(1).WriteArray(healthy_veg[0])
        healthy_veg_ds.GetRasterBand(2).WriteArray(healthy_veg[1])
        healthy_veg_ds.GetRasterBand(3).WriteArray(healthy_veg[2])
        healthy_veg_ds.FlushCache()

    else:
        raise Exception("Failed to create TIFF file")

    return healthy_veg_path

get_ndmi(nir_band, swir_band, georef)

Calculates the Normalized Difference Moisture Index (NDMI) from Near Infrared (NIR) and Shortwave Infrared (SWIR) bands.

Parameters:

Name Type Description Default
nir_band numpy.ndarray

A 2D or 3D array containing the Near Infrared band data.

required
swir_band numpy.ndarray

A 2D or 3D array containing the Shortwave Infrared band data.

required
georef osgeo.gdal.Dataset

A GDAL dataset containing georeferencing information for the input bands.

required

Returns:

Type Description
str

The path to the created NDMI file.

Exceptions:

Type Description
ValueError

If the input arrays are not of the same shape.

ValueError

If the input arrays have different data types.

ValueError

If the input arrays have invalid values.

ImportError

If the GDAL library is not installed.

Examples:

nir_band = gdal.Open('path/to/nir_band.tif').ReadAsArray() swir_band = gdal.Open('path/to/swir_band.tif').ReadAsArray() georef = gdal.Open('path/to/reference.tif')

Generate an NDMI composite image and download it

ndmi_path = get_NDMI(red_band, nir_band, georef)

Download the file at ndvi_path

Source code in bassmap/bassmap.py
def get_ndmi(nir_band, swir_band, georef):

    """
    Calculates the Normalized Difference Moisture Index (NDMI) from Near Infrared (NIR) and Shortwave Infrared (SWIR) bands.

    Args:
        nir_band (numpy.ndarray): A 2D or 3D array containing the Near Infrared band data.
        swir_band (numpy.ndarray): A 2D or 3D array containing the Shortwave Infrared band data.
        georef (osgeo.gdal.Dataset): A GDAL dataset containing georeferencing information for the input bands.

    Returns:
        str: The path to the created NDMI file.

    Raises:
        ValueError: If the input arrays are not of the same shape.
        ValueError: If the input arrays have different data types.
        ValueError: If the input arrays have invalid values.
        ImportError: If the GDAL library is not installed.

    Examples:
        nir_band = gdal.Open('path/to/nir_band.tif').ReadAsArray()
        swir_band = gdal.Open('path/to/swir_band.tif').ReadAsArray()
        georef = gdal.Open('path/to/reference.tif')

        # Generate an NDMI composite image and download it
        ndmi_path = get_NDMI(red_band, nir_band, georef)
        # Download the file at ndvi_path
    """

    from osgeo import gdal

    # Scale the input bands to the range of 0-255
    nir_band = (nir_band / 65535.0) * 255.0
    swir_band = (swir_band / 65535.0) * 255.0

    # Calculate the NDMI from the NIR and SWIR bands
    ndmi = np.empty_like(nir_band, dtype=np.float32)
    ndmi.fill(np.nan)
    valid = np.logical_and(nir_band != 0, swir_band != 0)
    ndmi[valid] = (nir_band[valid] - swir_band[valid]) / (nir_band[valid] + swir_band[valid])

    # Get the dimensions and georeferencing information from one of the input files
    xsize = georef.RasterXSize
    ysize = georef.RasterYSize
    proj = georef.GetProjection()
    geotrans = georef.GetGeoTransform()
    driver = gdal.GetDriverByName('GTiff')

    # Create a new TIFF file and write the NDVI array to it
    ndmi_path = os.path.join(tmp_dir.name, 'ndmi_composite.tif')
    ndmi_ds = driver.Create(ndmi_path, xsize, ysize, 1, gdal.GDT_Float32)

    if ndmi_ds is not None:
        ndmi_ds.SetProjection(proj)
        ndmi_ds.SetGeoTransform(geotrans)
        ndmi_ds.GetRasterBand(1).WriteArray(ndmi)
        ndmi_ds.FlushCache()
    else:
        raise Exception("Failed to create TIFF file")

    return ndmi_path

get_truecolor(red_band, green_band, blue_band, georef)

Generate a true-color composite image from red, green, and blue bands.

Parameters:

Name Type Description Default
red_band ndarray

A 2D numpy array representing the red band.

required
green_band ndarray

A 2D numpy array representing the green band.

required
blue_band ndarray

A 2D numpy array representing the blue band.

required
georef osgeo.gdal.Dataset

A GDAL dataset containing georeferencing information for the input bands.

required

Returns:

Type Description
str

The path to the created true color file.

Exceptions:

Type Description
ValueError

If the input arrays are not of the same shape.

ValueError

If the input arrays have different data types.

ValueError

If the input arrays have invalid values.

ImportError

If the GDAL library is not installed.

Examples:

Load the red, green, and blue bands from GeoTIFF files

red_band = gdal.Open('path/to/red_band.tif').ReadAsArray() green_band = gdal.Open('path/to/green_band.tif').ReadAsArray() blue_band = gdal.Open('path/to/blue_band.tif').ReadAsArray() georef = gdal.Open('path/to/reference.tif')

Generate a true-color composite image and download it

true_color_path = true_color_path(red_band, green_band, blue_band, georef)

Download the file at true_color_path

Source code in bassmap/bassmap.py
def get_truecolor(red_band, green_band, blue_band, georef):

    """Generate a true-color composite image from red, green, and blue bands.

    Args:
        red_band (ndarray): A 2D numpy array representing the red band.
        green_band (ndarray): A 2D numpy array representing the green band.
        blue_band (ndarray): A 2D numpy array representing the blue band.
        georef (osgeo.gdal.Dataset): A GDAL dataset containing georeferencing information for the input bands.

    Returns:
        str: The path to the created true color file.

    Raises:
        ValueError: If the input arrays are not of the same shape.
        ValueError: If the input arrays have different data types.
        ValueError: If the input arrays have invalid values.
        ImportError: If the GDAL library is not installed.

    Example:
        # Load the red, green, and blue bands from GeoTIFF files
        red_band = gdal.Open('path/to/red_band.tif').ReadAsArray()
        green_band = gdal.Open('path/to/green_band.tif').ReadAsArray()
        blue_band = gdal.Open('path/to/blue_band.tif').ReadAsArray()
        georef = gdal.Open('path/to/reference.tif')

        # Generate a true-color composite image and download it
        true_color_path = true_color_path(red_band, green_band, blue_band, georef)
        # Download the file at true_color_path
    """

    from osgeo import gdal

    # Combine the three bands into a single 3D array with 3 bands
    true_color = np.array([red_band, green_band, blue_band], dtype=np.uint16)

    # Get the dimensions and georeferencing information from one of the input files
    xsize = georef.RasterXSize
    ysize = georef.RasterYSize
    proj = georef.GetProjection()
    geotrans = georef.GetGeoTransform()
    driver = gdal.GetDriverByName('GTiff')

    # Create a new TIFF file and write the NDVI array to it
    true_color_path = os.path.join(tmp_dir.name, 'true_color_comp.tif')
    true_color_ds = driver.Create(true_color_path, xsize, ysize, 3, gdal.GDT_Float32)

    if true_color_ds is not None:
        true_color_ds.SetProjection(proj)
        true_color_ds.SetGeoTransform(geotrans)
        true_color_ds.GetRasterBand(1).WriteArray(true_color[0])
        true_color_ds.GetRasterBand(2).WriteArray(true_color[1])
        true_color_ds.GetRasterBand(3).WriteArray(true_color[2])
        true_color_ds.FlushCache()

    else:
        raise Exception("Failed to create TIFF file")

    return true_color_path

Last update: 2023-05-11