ShadowFinder

repository·main·Indexed 20 days ago

https://github.com/bellingcat/shadowfinder

A tool for geolocation purposes used to estimate locations on Earth where a shadow of a specific length or a specific sun altitude angle could occur at a given date and time. Available as a Python library, CLI, and Google Colab notebook, version 0.7.0.

Tokens
3.8K
Snippets
14
Records
21
Agent score
68%

What's inside ShadowFinder

  1. Manage the timezone grid for performance

    main

    ShadowFinder uses a timezone grid to speed up calculations. To avoid the resource-intensive process of generating this grid every time you run a search, you should generate it once and save it to a file.

    In the Python API, use these methods:

    • generate_timezone_grid(): Generates the grid.
    • save_timezone_grid(): Saves the grid to timezone_grid.json.
    • load_timezone_grid(): Attempts to load an existing grid from a file.
  2. Use ShadowFinder as a Python Library

    main

    You can integrate ShadowFinder into your Python scripts by importing the ShadowFinder class. The workflow involves initializing the finder, managing the timezone grid (to optimize performance), setting the scenario details, running the calculation, and plotting the results.

    from shadowfinder import ShadowFinder
    
    finder = ShadowFinder()
    
    # Use a pre-generated timezone grid to save time
    try:
        finder.load_timezone_grid()
    except FileNotFoundError:
        finder.generate_timezone_grid()
        finder.save_timezone_grid() # saves to timezone_grid.json
    
    # Set up the scenario
    # You must provide either (object_height AND shadow_length) OR sun_altitude_angle
    finder.set_details(
        date_time=date_time, # datetime object with no timezone awareness
        object_height=object_height, # object height in arbitrary units
        shadow_length=shadow_length, # shadow length in arbitrary units
        time_format=time_type, # string, either 'local' or 'utc'
        sun_altitude_angle=sun_altitude_angle, # altitude angle of the sun, in degrees above the horizon
    )
    
    # Run the finder
    finder.find_shadows()
    
    # Access the resulting figure
    fig = finder.plot_shadows()
  3. Use ShadowFinder to estimate shadow locations

    main

    ShadowFinder is a tool used for geolocation by estimating points on the Earth's surface where a shadow of a specific length (or sun angle) could occur at a given date and time.

    Important Requirement: Shadow length must be measured at right angles to the object. If the image has perspective distortion, you must correct it before using these measurements.

    To use the tool, you can provide either:

    1. Object and shadow measurements: An object_height and a shadow_length (measured in arbitrary units, as long as they are consistent).
    2. Sun elevation angle: The sun_altitude_angle directly in degrees.

    You must also provide the date and time. The time_type can be set to either utc or local.

    from shadowfinder import ShadowFinder
    import datetime
    
    # Setup date and time
    date_time = datetime.datetime(2024, 2, 29, 12, 0, 0)
    
    finder = ShadowFinder()
    
    # Configure the scenario
    finder.set_details(
        date_time=date_time,
        object_height=10,
        shadow_length=8,
        time_format="utc",
        sun_altitude_angle=None
    )
    
    # Execute search and visualize
    finder.find_shadows()
    fig = finder.plot_shadows()
  4. Configure ShadowFinder scenario details

    main

    The set_details method defines the physical and temporal parameters of the shadow. You have two ways to define the scenario:

    1. Using dimensions: Provide object_height and shadow_length (both in arbitrary units).
    2. Using solar angle: Provide sun_altitude_angle (the angle of the sun in degrees above the horizon).

    Required parameters:

    • date_time: A datetime object (must be timezone-unaware).
    • time_format: A string, either 'local' or 'utc'.

    Note: object_height and shadow_length must use the same arbitrary units.

  5. Use the ShadowFinder CLI

    main

    ShadowFinder provides a command-line interface. Note: The CLI is slower than the Python API because it regenerates the timezone grid on every run.

    Estimate location using object dimensions

    Use the find command with the following positional arguments: OBJECT_HEIGHT SHADOW_LENGTH DATE TIME

    shadowfinder find 10 5 2024-02-29 13:59:59 --time_format=utc

    Estimate location using sun altitude

    Use the find_sun command with the following positional arguments: SUN_ALTITUDE_ANGLE DATE TIME

    shadowfinder find_sun 50 2024-02-29 13:59:59 --time_format=utc

    Help commands

    To see more information, run:

    shadowfinder find --help
    shadowfinder find_sun --help
  6. Update ShadowFinder details with set_details()

    main

    Use set_details() to update the parameters of an existing ShadowFinder instance. This method handles the logic of clearing mutually exclusive parameters (e.g., if you set sun_altitude_angle, it clears object_height and shadow_length).

    Arguments:

    • date_time: The observation timestamp.
    • object_height: Height of the object.
    • shadow_length: Length of the shadow.
    • time_format: 'utc' or 'local'.
    • sun_altitude_angle: Sun altitude in degrees (0-90).

    Validation:

    • time_format must be 'utc' or 'local'.
    • sun_altitude_angle must be between 0 and 90.
    • object_height and shadow_length must both be provided or both be None.
    finder.set_details(
        date_time=new_datetime,
        object_height=5.0,
        shadow_length=3.0,
        time_format='local'
    )
  7. Calculate shadow likelihoods with find_shadows()

    main

    The find_shadows() method evaluates the sun's position across a global grid to determine where the observed shadow parameters are most likely to occur.

    Workflow:

    1. It ensures a timezone grid is loaded or generated.
    2. It calculates the sun's altitude at every grid point for the given date_time.
    3. It computes location_likelihoods based on the difference between observed and calculated values.

    Likelihood Calculation:

    • If using height/length: It calculates the relative difference: (calculated_shadow_length - observed_shadow_length) / observed_shadow_length.
    • If using sun altitude angle: It calculates the relative difference: (calculated_altitude - observed_altitude) / observed_altitude.

    Points where the sun is below the horizon are marked as NaN.

  8. Manage timezone grids in ShadowFinder

    main

    ShadowFinder uses a timezone grid to perform its calculations. If a grid is not available, it can be generated and saved locally to speed up subsequent runs.

    • finder.load_timezone_grid(): Attempts to load an existing timezone grid.
    • finder.generate_timezone_grid(): Generates a new timezone grid if one is not found.
    • finder.save_timezone_grid(): Saves the generated grid to a file.
  9. Configure ShadowFinder details with set_details()

    main

    The set_details() method configures the parameters for the shadow search.

    Parameters:

    • date_time (datetime): The specific date and time of interest.
    • object_height (float/int): Height of the object in arbitrary units.
    • shadow_length (float/int): Length of the shadow in arbitrary units (must be at right angles to the object).
    • time_format (str): Either 'utc' or 'local'.
    • sun_altitude_angle (float/int, optional): The elevation angle to the sun in degrees. If provided, object_height and shadow_length are not required.