diff --git a/docs/Web-Mapping-and-Geovisualisation.pdf b/docs/Web-Mapping-and-Geovisualisation.pdf index 96f77ad..e6498ea 100644 Binary files a/docs/Web-Mapping-and-Geovisualisation.pdf and b/docs/Web-Mapping-and-Geovisualisation.pdf differ diff --git a/docs/labs/w09_advanced.html b/docs/labs/w09_advanced.html index 688738b..e08a729 100644 --- a/docs/labs/w09_advanced.html +++ b/docs/labs/w09_advanced.html @@ -244,7 +244,7 @@

Table of contents

  • 8.2 Animations in Folium
  • @@ -291,7 +291,7 @@

    8  # pip install geoviews # pip install movingpandas -
    +
    import pandas as pd, geopandas as gpd
     import movingpandas as mpd
     
    @@ -318,99 +318,118 @@ 

    notebook created by Anita Graser.

    +

    Parts of this section of the notebook have been readapted from this notebook created by Anita Graser.

    8.1.1 Downaloding GPS traces from the OpenStreetMap

    -

    def bbox_to_osm_format(bbox): ““” Convert bounding box coordinates to OSM API format.

    -
    Parameters
    -----------
    -bbox: tuple
    -    A tuple containing the bounding box coordinates in the order (north, south, east, west).
    -
    -Returns
    --------
    -bbox_str: str
    -    A string representing the bounding box in the format "west,south,east,north".
    -"""
    -north, south, east, west = bbox
    -bbox = f"{west},{south},{east},{north}"
    -return bbox

    We can try to get OSM traces around the Uni of Liverpool Campus. Feel free to change the area.

    -
    -
    import osmnx as ox
    -# Define the place name
    -place_name = "University of Liverpool, UK"
    -
    -# Get the latitude and longitude
    -latitude, longitude = ox.geocoder.geocode(place_name)
    -
    -# Create the bbox
    -bbox = ox.utils_geo.bbox_from_point((latitude, longitude), dist = 1500)
    -bbox = bbox_to_osm_format(bbox) # needs to be {west},{south},{east},{north} for OSM Api
    -
    -

    The get_osm_traces function below retrieves GPS traces from OpenStreetMap within a specified bounding box, processing up to a user-defined maximum number of pages. It uses a while loop to fetch and parse GPS data into GeoDataFrames, querying the OSM API by incrementally updating the page number until no more data is available or the maximum page limit is reached.

    -

    Upon fetching the data, the function concatenates these individual GeoDataFrames into a single comprehensive GeoDataFrame. Before returning the final GeoDataFrame, it cleans the dataset by dropping a predefined list of potentially irrelevant or empty columns.

    -
    -
    def get_osm_traces(max_pages = 2,  bbox='16.18, 48.09, 16.61, 48.32'):
    +
    +
    import osmnx as ox
    +# Define the place name
    +place_name = "University of Liverpool, UK"
    +
    +# Get the latitude and longitude
    +latitude, longitude = ox.geocoder.geocode(place_name)
    +
    +# Create the bbox
    +bbox = ox.utils_geo.bbox_from_point((latitude, longitude), dist = 1500)
    +bbox
    +
    +
    (53.420732655032396,
    + 53.39375304496761,
    + -2.9432044446907044,
    + -2.988462877347038)
    +
    +
    +

    We need the bbox_to_osm_format function below to convert our bounding box coordinates into the format required by the OpenStreetMap (OSM) API. The input tuple contains coordinates in the order (north, south, east, west). The function rearranges these values into the string “west,south,east,north”, which is the format expected by OSM for API queries.

    +
    +
    def bbox_to_osm_format(bbox):
         """
    -    Retrieve OpenStreetMap GPS traces within a specified bounding box.
    +    Convert bounding box coordinates to OSM API format.
     
         Parameters
         ----------
    -    max_pages: int, optional
    -        The maximum number of pages to retrieve. Defaults to 2.
    -    bbox: str, optional
    -        The bounding box coordinates in the format 'west, south, east, north'. Defaults to '16.18, 48.09, 16.61, 48.32'.
    -
    -    Returns
    -    -------
    -    final_gdf: GeoDataFrame
    -        A GeoDataFrame containing the retrieved GPS traces.
    -    """
    -    
    -    all_data = []
    -    page = 0
    -    
    -    while (True) and (page <= max_pages):
    -        # Constructing the URL to query OpenStreetMap API for GPS trackpoints within a specified bounding box and page number
    -        url = f'https://api.openstreetmap.org/api/0.6/trackpoints?bbox={bbox}&page={page}'
    -        
    -        # Sending a GET request to the constructed URL
    -        response = requests.get(url)
    -        
    -        # Checking if the response status code is not 200 (indicating success) or if the response content is empty
    -        # If either condition is met, the loop breaks, indicating no more data to retrieve
    -        if response.status_code != 200 or not response.content:
    -            break
    -        
    -        # Reading the content of the response, which contains GPS trackpoints data, into a GeoDataFrame
    -        # The 'layer' parameter specifies the layer within the GeoDataFrame where trackpoints will be stored
    -        gdf = gpd.read_file(BytesIO(response.content), layer='track_points')
    -
    -        if gdf.empty:
    -            break
    -            
    -        all_data.append(gdf)
    -        page += 1
    -        
    -    # Concatenate all GeoDataFrames into one
    -    final_gdf = gpd.GeoDataFrame(pd.concat(all_data, ignore_index=True))
    -    
    -    # dropping empty columns
    -    columns_to_drop = ['ele', 'course', 'speed', 'magvar', 'geoidheight', 'name', 'cmt', 'desc',
    -                       'src', 'url', 'urlname', 'sym', 'type', 'fix', 'sat', 'hdop', 'vdop',
    -                       'pdop', 'ageofdgpsdata', 'dgpsid']
    -    
    -    final_gdf = final_gdf.drop(columns=[col for col in columns_to_drop if col in final_gdf.columns])
    -    return final_gdf
    + bbox: tuple + A tuple containing the bounding box coordinates in the order (north, south, east, west). + + Returns + ------- + bbox_str: str + A string representing the bounding box in the format "west,south,east,north". + """ + north, south, east, west = bbox + bbox = f"{west},{south},{east},{north}" + return bbox
    +
    +
    +
    bbox = bbox_to_osm_format(bbox) # needs to be {west},{south},{east},{north} for OSM Api
    +bbox
    +
    +
    '-2.988462877347038,53.39375304496761,-2.9432044446907044,53.420732655032396'
    +
    +
    +

    The get_osm_traces function below retrieves GPS traces from OpenStreetMap within a specified bounding box, processing up to a user-defined maximum number of pages. It uses a while loop to fetch and parse GPS data into GeoDataFrames, querying the OSM API by incrementally updating the page number until no more data is available or the maximum page limit is reached.

    +

    Upon fetching the data, the function concatenates these individual GeoDataFrames into a single comprehensive GeoDataFrame. Before returning the final GeoDataFrame, it cleans the dataset by dropping a predefined list of potentially irrelevant or empty columns.

    +
    +
    def get_osm_traces(max_pages = 2,  bbox='16.18, 48.09, 16.61, 48.32'):
    +    """
    +    Retrieve OpenStreetMap GPS traces within a specified bounding box.
    +
    +    Parameters
    +    ----------
    +    max_pages: int, optional
    +        The maximum number of pages to retrieve. Defaults to 2.
    +    bbox: str, optional
    +        The bounding box coordinates in the format 'west, south, east, north'. Defaults to '16.18, 48.09, 16.61, 48.32'.
    +
    +    Returns
    +    -------
    +    final_gdf: GeoDataFrame
    +        A GeoDataFrame containing the retrieved GPS traces.
    +    """
    +    
    +    all_data = []
    +    page = 0
    +    
    +    while (True) and (page <= max_pages):
    +        # Constructing the URL to query OpenStreetMap API for GPS trackpoints within a specified bounding box and page number
    +        url = f'https://api.openstreetmap.org/api/0.6/trackpoints?bbox={bbox}&page={page}'
    +        
    +        # Sending a GET request to the constructed URL
    +        response = requests.get(url)
    +        
    +        # Checking if the response status code is not 200 (indicating success) or if the response content is empty
    +        # If either condition is met, the loop breaks, indicating no more data to retrieve
    +        if response.status_code != 200 or not response.content:
    +            break
    +        
    +        # Reading the content of the response, which contains GPS trackpoints data, into a GeoDataFrame
    +        # The 'layer' parameter specifies the layer within the GeoDataFrame where trackpoints will be stored
    +        gdf = gpd.read_file(BytesIO(response.content), layer='track_points')
    +
    +        if gdf.empty:
    +            break
    +            
    +        all_data.append(gdf)
    +        page += 1
    +        
    +    # Concatenate all GeoDataFrames into one
    +    final_gdf = gpd.GeoDataFrame(pd.concat(all_data, ignore_index=True))
    +    
    +    # dropping empty columns
    +    columns_to_drop = ['ele', 'course', 'speed', 'magvar', 'geoidheight', 'name', 'cmt', 'desc',
    +                       'src', 'url', 'urlname', 'sym', 'type', 'fix', 'sat', 'hdop', 'vdop',
    +                       'pdop', 'ageofdgpsdata', 'dgpsid']
    +    
    +    final_gdf = final_gdf.drop(columns=[col for col in columns_to_drop if col in final_gdf.columns])
    +    return final_gdf

    We initially download 2 pages of GSP traces. We can try with higher numbers to get more data. More recent traces are downloaded first.

    -
    -
    max_pages = 2 # pages of data, 
    -gps_track_points = get_osm_traces(max_pages, bbox)
    +
    +
    max_pages = 2 # pages of data, 
    +gps_track_points = get_osm_traces(max_pages, bbox)
    -
    -
    gps_track_points.head()
    +
    +
    gps_track_points.head()
    @@ -473,12 +492,12 @@

    -
    gps_track_points.plot(markersize = 0.5) # still are trackpoints still
    +
    +
    gps_track_points.plot(markersize = 0.5) # still are trackpoints still
    -

    +

    @@ -489,16 +508,16 @@

    -
    osm_traces = mpd.TrajectoryCollection(gps_track_points, 'track_fid', t='time')
    -print(f'The OSM traces download contains {len(osm_traces)} tracks')
    +
    +
    osm_traces = mpd.TrajectoryCollection(gps_track_points, 'track_fid', t='time')
    +print(f'The OSM traces download contains {len(osm_traces)} tracks')
    The OSM traces download contains 14 tracks
    -
    -
    for track in osm_traces: 
    -    print(f'Track {track.id}: length={track.get_length(units="km"):.2f} km')
    +
    +
    for track in osm_traces: 
    +    print(f'Track {track.id}: length={track.get_length(units="km"):.2f} km')
    Track 0: length=9.96 km
     Track 1: length=3.02 km
    @@ -518,13 +537,13 @@ 

    -
    from holoviews import opts, dim
    -import hvplot.pandas 
    -
    -plot_defaults = {'linewidth':5, 'capstyle':'round', 'figsize':(9,3), 'legend':True}
    -opts.defaults(opts.Overlay(active_tools=['wheel_zoom'], frame_width=500, frame_height=400))
    -hvplot_defaults = {'tiles':None, 'cmap':'Viridis', 'colorbar':True}
    +
    +
    from holoviews import opts, dim
    +import hvplot.pandas 
    +
    +plot_defaults = {'linewidth':5, 'capstyle':'round', 'figsize':(9,3), 'legend':True}
    +opts.defaults(opts.Overlay(active_tools=['wheel_zoom'], frame_width=500, frame_height=400))
    +hvplot_defaults = {'tiles':None, 'cmap':'Viridis', 'colorbar':True}