Authors: Jacob Mitchell
Date: 4/19/26
Our servers spin down with inactivity, so please be sure to allow for an additional 60 seconds upon making your first request to our backend.
https://environmentscreen.onrender.com
This program is a prototype application designed to help construction planners identify potential environmental risks before beginning a project. The system analyzes biodiversity data from the Global Biodiversity Information Facility (GBIF) and cross references it with the Illinois Endangered Species list to identify protected species near a proposed construction site.
This tool performs a preliminary environmental screening by checking for documented or sighted species occurrences within a specified geographic radius.
Construction projects may be delayed or halted if endangered or threatened species are present near a site. Currently, this review process often requires manual research across multiple datasets.
Our program automates this first step by:
- Accepting a project location
- Searching GBIF biodiversity databases for species sightings
- Comparing detected species with the Illinois endangered species list
- Returning flagged species that may impact a project plan
- Provide additional context to flagged species to user with additional information on how it may interact with their construction process
This version focuses on the data pipeline / detection logic / additional ecological analysis by OpenRouter api calls / frontend
- Scrape the latest Illinois Natural Heritage species-by-county dataset into
data/IsEndangered.csv- Script located at
scripts/unfiltered_species.py
- Script located at
- Translate Illinois Endangered list to taxonIDs saving a CSV with scientific names and their corresponding taxonID
- Script located at
scripts/build_taxon_lookup.py, output written todata/IllinoisTaxonLookup.csv
- Script located at
- Supply an address or coordinates and a radius in miles (address search powered by MapTiler Geocoding API)
- The program then computes a bounding box (note this bounding box is currently square for simplicity)
- Check Redis cache — if a matching scan exists for the same location and radius, return the cached result immediately
- Make a GBIF call to return all species within the given bounding box (occurrences filtered to year 2000–2026)
- Cross checks returned species with precomputed
data/IllinoisTaxonLookup.csv - Send batch request to OpenRouter for additional construction and species context (capped with
.envMAX_SPECIES_FOR_AI, default=3) - Store result in Redis cache (24-hour TTL)
- Display flagged results to user
Used for retrieving species occurrence records based on geographic location.
GBIF API endpoints used:
Occurrence Search
https://api.gbif.org/v1/occurrence/search
Species Name Matching
- Only used during precomputed
IllinoisTaxonLookup.csv
https://api.gbif.org/v1/species/match
Scraped from the Illinois Natural Heritage species-by-county dataset via
scripts/unfiltered_species.py, which writesdata/IsEndangered.csv. Re-run this script (followed byscripts/build_taxon_lookup.py) to refresh the dataset.
- Local CSV dataset containing endangered and threatened species observed in Illinois.
Example structure:
"County","Scientific Name","Common Name","State Status","Informal Taxonomy","Last Observed","# of Records"
Example entries:
- Pulaski, Tilia americana var. heterophylla, White Basswood, LE, Dicots, 5/7/2005, 1
- Piatt, Phlox pilosa ssp. sangamonensis, Sangamon Phlox, LE, Dicots, 6/4/2020, 4
The program precomputes a translated list, scientific name followed by taxonID, prior to user input to allow for faster runtimes
Example entries in precomputed translation csv:
- Justicia ovata,2393
- Kinosternon flavescens,2442437
- The
/geocode/searchendpoint accepts a plain-text address and returns coordinates, powered by the MapTiler Geocoding API. /geocode/reverseaccepts coordinates and returns a human-readable address label.- Both endpoints are Redis-cached (24-hour TTL) to avoid duplicate lookups.
- A bounding box is generated from the radius to perform more reliable GBIF searches.
Requires a running Redis instance. See Environment Variables for setup.
- Scan results are cached by location and radius so repeated requests for the same area skip all GBIF and OpenRouter calls entirely.
- Cache key:
scan:{lat}:{lon}:{radius}— coordinates rounded to 3 decimal places (~111 m precision), radius rounded to 1 decimal place - Cache TTL: 24 hours
- Cache key:
- Geocode and reverse-geocode responses are also cached in Redis (24-hour TTL) so address lookups aren't repeated unnecessarily.
- All Redis operations degrade gracefully — if Redis is unavailable, the app continues without caching and logs a
[REDIS ERROR]message rather than crashing. - In local development with Redis running (WSL2:
sudo service redis-server start), cache hits will appear in the terminal as[SCAN CACHE HIT].
To prevent automated abuse of our environmental screening API, I've implemented human verification along with rate limiting.
- Features Added:
- Cloudflare Turnstile integration
- Invisible / low friction human verification (no captchas)
- Token generated on frontend then verified on backend
- Backend token validation
- All
/scan/startrequests now require a valid Turnstile token
- All
- Rate limiting
- Limits applied per IP to prevent excessive calling abuse
- CORS hardened
- Restricted to approved frontend origin only
- Cloudflare Turnstile integration
- How it works:
- User submits scan request from frontend
- Turnstile generates a verification token
- Token is sent with the request to the backend
- Backend validates token with Cloudflare
- If valid proceed, if not reject
- A Provide Feedback button lives in the bottom-right of the site, themed to match the rest of the UI.
- Users can submit a title, a body (what's good, bad, or wanted), an optional 1–5 star rating, and an optional contact email for a reply.
- Submissions are auto-populated into the repository's GitHub Issues (labeled
feedback). - The GitHub Personal Access Token is held only on the backend (
GITHUB_FEEDBACK_PAT) — never shipped to the browser. The frontend posts to the/feedbackendpoint, which calls the GitHub Issues API server-side. - Like
/scan/start, the endpoint is protected by Cloudflare Turnstile verification and a per-IP rate limit (5/hour) to prevent abuse.
- Species names are resolved to their taxonIDs prior to user input to improve performance.
- Species are only considered from the official Illinois Endangered Species List, ignoring all other occurences of different species from GBIF
- After endangered species are detected, our system will generate additional context using OpenRouter api to return more information to the user
- The module
open_router_context.pyanalyzes each flagged species in a batch call with a max count being defined in the .env by the runner - The AI analysis may include
- Important ecological behaviors
- Breeding / migration seasonal considerations
- Construction activities that are deemed most disruptive
- A cautious recommendation for when construction may be the least disruptive
- Example output:
Myotis sodalis
Indiana bats are particularly sensitive to disturbance during maternity
season when females form roosting colonies in trees. Construction
activities involving tree clearing, heavy noise, or nighttime lighting
during late spring and summer may disrupt these colonies. If possible,
major disturbance activities may be less disruptive outside the
maternity season, typically late fall through winter.
- To ensure performance remains high and reduce costs, the program will send all detected species in one single OpenRouter request rather than a request for each detected animal
Warning
GBIF sightings may not always include subspecies names as seen in Illinois Endangered Species List
Example:
GBIF may report:
Tilia americana
Illinois listing:
Tilia americana var. heterophylla
In these cases the species level occurrence is used
This tool is intended for early stage environmental screening, not regulatory compliance, as we cannot guarantee the absence of false positives or false negatives.
Senior-Project/
├── app.py # FastAPI application entry point
├── scan.py # Scan endpoint + background job runner
├── geocode.py # Geocode / reverse-geocode endpoints
├── GBIF.py # GBIF API interaction + species matching logic
├── openai_species_context.py # OpenAI batch context analysis
├── open_router_context.py # OpenRouter batch context analysis
├── redis_client.py # Redis wrapper (cache_get / cache_set / cache_delete)
├── limiter.py # SlowAPI rate limiter configuration
├── data/
│ ├── IsEndangered.csv # Raw Illinois Endangered Species list
│ └── IllinoisTaxonLookup.csv # Precomputed scientific name → taxonID lookup
├── scripts/
│ ├── unfiltered_species.py # Scrapes Illinois Natural Heritage → data/IsEndangered.csv
│ └── build_taxon_lookup.py # Script to regenerate IllinoisTaxonLookup.csv
├── frontend/ # React + Vite frontend (Leaflet map, scan UI)
├── tests/
│ ├── test_scan.py
│ ├── test_geocode.py
│ ├── test_GBIF.py
│ ├── test_open_router_context.py
│ └── test_openai_species_context.py
├── .github/workflows/test.yml # GitHub Actions CI workflow
├── conftest.py # Pytest fixtures (fakeredis autouse)
├── pytest.ini # Pytest configuration + custom markers
├── requirements.txt
├── requirements-dev.txt
├── environment.yml
└── .env.example
conda env create -f environment.yml
Or with pip directly:
pip install -r requirements.txt
pip install -r requirements-dev.txt # for development / testing
Find our frontend here: https://environmentscreen.onrender.com
To run the backend locally:
Within conda GBIF_env (and with
.envfile keys / parameters set):
uvicorn app:app --reload
To refresh the Illinois Endangered Species dataset from the Illinois Natural Heritage source, then regenerate the taxon lookup:
python scripts/unfiltered_species.py
python scripts/build_taxon_lookup.py
To run the frontend locally (Vite dev server, default port 5173):
cd frontend
npm install
npm run dev
The project uses pytest with fakeredis for test isolation — no real Redis server is required to run the tests.
pytest tests/
Custom markers are defined in pytest.ini:
integration— tests that hit live external APIs. Skip with-m "not integration"(this is what CI runs).slow— marks slow-running tests.
A GitHub Actions CI workflow (.github/workflows/test.yml) runs the non-integration test suite automatically on every push and pull request to main.
- Our project uses environmental variables such as max species openai call count and of course our api key
- These variables are automatically read by python SDK through vscode reading each members
.envfile - To configure or view format please see
.env.example— this can be created withcp .env.example .envbefore adding your own keys / parameters.
- These variables are automatically read by python SDK through vscode reading each members
| Variable | Description | Default |
|---|---|---|
OPENAI_API_KEY |
OpenAI API key for ecological context analysis | — |
OPENROUTER_API_KEY |
OpenRouter API key for ecological context analysis | — |
MAX_SPECIES_FOR_AI |
Max species sent to OpenRouter per scan | 3 |
MAPTILER_API_KEY |
MapTiler API key for geocoding | — |
TURNSTILE_SECRET_KEY |
Cloudflare Turnstile secret for bot protection | — |
FRONTEND_ORIGIN |
Allowed CORS origin | http://localhost:5173 |
REDIS_URL |
Redis connection URL | redis://localhost:6379 |
GITHUB_FEEDBACK_PAT |
Fine-grained GitHub PAT used server-side to open feedback issues (scope: Issues → Read and write, on the target repo only) | — |
GITHUB_FEEDBACK_REPO |
Target repo for feedback issues, in owner/repo form |
— |
For Render.com deployments, set
REDIS_URLto the internal Redis URL provided by your Render Redis service —localhostwill not work in a hosted environment.
- Schedule daily runs of
scripts/unfiltered_species.py+scripts/build_taxon_lookup.pyto keepIsEndangered.csvandIllinoisTaxonLookup.csvcurrent - Export construction timeline recommendations
- Improve AI ecological analysis using external species data sources (Wikipedia, species databases)
- Expand coverage beyond Illinois to other state endangered species lists
- Our program provides informational screening only
- Results should always be verified with environmental professionals and official regulatory databases before making construction decisions.