Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pypi-publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ jobs:
uses: JRubics/poetry-publish@v1.17
with:
pypi_token: ${{ secrets.PYPI_TOKEN }}
python_version: 3.9
python_version: 3.10
2 changes: 1 addition & 1 deletion .github/workflows/tox.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:

strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]
python-version: ["3.10", "3.11", "3.12"]
fail-fast: false

steps:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A lightweight tool and Google Colab notebook for estimating the points on the Ea

Using an object's height, the length of its shadow, the date and the time, ShadowFinder estimates the possible locations where that shadow could occur. These possible locations are shown as a bright band on a map of the Earth:

![ExampleShadowFinderOutput](https://github.com/bellingcat/ShadowFinder/assets/54807169/391c9b54-d5b4-463f-9c09-94ff1fec6ee4)
![ExampleShadowFinderOutput](https://github.com/user-attachments/assets/1620de90-3544-4678-856a-9ac5a56655a4)


## Usage - Google Colab Notebook 🚀
Expand Down
4 changes: 1 addition & 3 deletions ShadowFinderColab.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@
"\n",
"Using an object's height and the length of its shadow (or the angle to the sun) with the date and the time, this code estimates the possible locations of that shadow.\n",
"\n",
"> <font color='#ffc107'>Important:</font> The shadow length must be measured at right angles to the object 📐 This means that you might have to correct for the perspective of an image before using this tool.\n",
"\n",
"> <font color='#ab0107'>Bug Workaround:</font> At the moment there is a bug with Shadow Finder which results in an error. You can still use the tool in Google Colab by running the tool, getting the error, then restarting the session. Do this by navigating to the 'Runtime' menu at the top of the window, then click 'Restart session'. When you run the tool a second time it should work."
"> <font color='#ffc107'>Important:</font> The shadow length must be measured at right angles to the object 📐 This means that you might have to correct for the perspective of an image before using this tool.\n"
]
},
{
Expand Down
1,702 changes: 889 additions & 813 deletions poetry.lock

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "ShadowFinder"
version = "0.5.0"
version = "0.6.0"
description = "Find possible locations of shadows."
authors = ["Bellingcat"]
license = "MIT License"
Expand All @@ -24,15 +24,15 @@ keywords=["shadow", "finder", "locator", "map"]
shadowfinder = "shadowfinder.__main__:main"

[tool.poetry.dependencies]
python = ">=3.9,<3.13"
python = ">=3.10,<3.13"
matplotlib = "^3.8"
basemap = "^1.4"
suncalc = "^0.1.3"
fire = "^0.5"
timezonefinder = "^6.5"
pandas = "^2.2"
numpy = "^1"
numpy = ">=1"
pytz = "^2024.1"
cartopy = "^0.24.1"

[tool.poetry.group.dev.dependencies]
black = "24.2.0"
Expand Down
59 changes: 40 additions & 19 deletions src/shadowfinder/shadowfinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
from mpl_toolkits.basemap import Basemap
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.io import DownloadWarning
from timezonefinder import TimezoneFinder
import json
from warnings import warn
from warnings import warn, filterwarnings
from math import radians


Expand Down Expand Up @@ -241,35 +243,54 @@ def find_shadows(self):
def plot_shadows(
self,
figure_args={"figsize": (12, 6)},
basemap_args={"projection": "cyl", "resolution": "c"},
projection="PlateCarree",
projection_args={},
):

fig = plt.figure(**figure_args)

# Add a simple map of the Earth
m = Basemap(**basemap_args)
m.drawcoastlines()
m.drawcountries()
# Set the a color scale and only show the values between 0 and 0.2

# Deal with the map projection
x, y = m(self.lons, self.lats)
# Create a custom LinearSegmented colormap
cmap = colors.LinearSegmentedColormap.from_list(
"custom_cmap",
[
# 0 is the peak likelihood, 1 is the low likelihood
(0, (1, 1, 0.75, 1)), # Light Yellow - peak likelihood
(0.05, (1, 1, 0, 1)), # Yellow - high likelihood
(0.2, (1, 0.5, 0, 1)), # Orange - low likelihood
(1, (1, 0, 0, 0)), # Transparent Red - no likelihood
],
N=256,
)

# Override the edge value of the cmap
cmap.set_over("white", alpha=0.5) # Day time colour
cmap.set_under("black", alpha=0.5) # Night time colour

# Set the a color scale and only show the values between 0 and 0.2
cmap = plt.cm.get_cmap("inferno_r")
norm = colors.BoundaryNorm(np.arange(0, 0.2, 0.02), cmap.N)

# Plot the data
m.pcolormesh(
x,
y,
np.abs(self.location_likelihoods),
# Create the map projection
filterwarnings("ignore", category=DownloadWarning)
ax = plt.axes(projection=getattr(ccrs, projection)(**projection_args))
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(
cfeature.BORDERS, linestyle="-", edgecolor="black", linewidth=0.5
)

# replace NaN values with a specific value (e.g. -1)
surface = np.abs(self.location_likelihoods)
surface = np.where(np.isnan(surface), -1, surface)

ax.pcolormesh(
self.lons,
self.lats,
surface,
cmap=cmap,
norm=norm,
alpha=0.7,
transform=ccrs.PlateCarree(),
)

# plt.colorbar(label='Relative Shadow Length Difference')

if self.sun_altitude_angle is not None:
plt_title = f"Possible Locations at {self.date_time.strftime('%Y-%m-%d %H:%M:%S')} {self.time_format.title()}\n(sun altitude angle: {self.sun_altitude_angle})"
else:
Expand Down
3 changes: 1 addition & 2 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
[tox]
envlist = py39, py310, py311, py312
envlist = py310, py311, py312
labels =
; Used to map GitHub workflow python version to tox env
3.9 = py39
3.10 = py310
3.11 = py311
3.12 = py312
Expand Down