Inspiration
West Nile virus remains a current public-health concern in New York City, and recent NYC Health Department reports have highlighted both human cases and widespread West Nile-positive mosquito activity across the five boroughs.
That made us interested in a simple question:
Can we use the mosquito surveillance data NYC already collects, along with weather conditions, to provide a more forward-looking view of mosquito activity?
We built Mosquito Watch to turn raw mosquito and weather data into something more accessible. Instead of only showing where West Nile-positive mosquitoes have already been found, our goal is to help users explore where elevated mosquito activity may occur next, understand the conditions behind that forecast, and ask questions about the data in plain language.
What it does
Mosquito Watch is an interactive NYC dashboard that provides next-week mosquito-risk forecasts by ZIP code.
Users can:
- Explore forecast risk across NYC on an interactive map
- Select a ZIP code and view its risk score and risk level
- See recent West Nile-positive mosquito detections
- Explore environmental indicators such as temperature, rainfall, and humidity
- View historical mosquito trends and day-by-day weather data throughout the week
- Receive a Gemini-generated explanation of the forecast
- Ask questions through a Gemini-powered chatbot
- Receive cached chatbot answers for repeated questions, reducing unnecessary Gemini API calls and token usage
The core prediction always comes from our machine-learning model:
XGBoost predicts; Gemini explains.
How we built it
We combined NYC mosquito surveillance data with historical weather data and engineered features related to:
- Recent positive mosquito detections
- Temperature
- Rainfall
- Humidity
- Geographic information
- Time of year
We trained an XGBoost classifier to predict whether mosquito activity would be elevated in the following week.
The model produces a probability:
[ P(\text{elevated mosquito activity next week}) ]
which we convert into a risk score:
[ \text{Risk Score} = P \times 100 ]
The score is then categorized as:
- Low: (0 \leq \text{score} < 25)
- Moderate: (25 \leq \text{score} < 50)
- Elevated: (50 \leq \text{score} < 75)
- High: (75 \leq \text{score} \leq 100)
Our application is divided into several layers:
Data and machine learning
- Python
- Pandas
- scikit-learn
- XGBoost
Backend
- FastAPI
- REST endpoints for:
- ZIP metadata
- Individual predictions
- Forecasts for all ZIP codes
- Historical trends
- AI/chatbot interactions
Frontend
- React
- TypeScript
- Interactive NYC visualization
- ZIP-level risk details
- Historical and daily data displays
AI
- Gemini API
- Natural-language forecast explanations
- Chatbot responses
Caching
- Redis
- Docker
- Repeated chatbot questions are cached so identical requests do not unnecessarily call Gemini again
We also separated the preprocessing pipeline from the XGBoost model and load the trained model through a portable JSON artifact, making backend inference more reliable across different environments.
Challenges we ran into
One of the biggest challenges was getting all four parts of the project — data, machine learning, backend, and frontend — to work together while different team members were developing them in parallel.
We also encountered a model portability issue. Our original combined model.joblib artifact failed to load reliably in another environment. We solved this by exporting the preprocessing pipeline separately and saving the XGBoost model in a portable JSON format.
Another challenge was making sure the model was truly predicting the following week. We had to carefully align the training data and historical features so the model did not accidentally use future information.
Gemini integration also required additional debugging as model availability and API interfaces changed. We migrated to the Gemini Interactions API and designed the backend so that Gemini failures never break the underlying mosquito forecast.
Finally, adding Redis introduced another integration layer. We had to make sure the cache could reduce repeated Gemini requests without becoming a dependency for the actual prediction system.
Accomplishments that we're proud of
We are proud that Mosquito Watch became a complete end-to-end application rather than just a machine-learning notebook.
Our backend can generate forecasts for 193 NYC ZIP codes, and the frontend turns those predictions into an interactive map and detailed ZIP-level views.
We are especially proud of the separation between predictive AI and generative AI.
The XGBoost model is the only system that calculates:
- Risk score
- Risk level
- Forecast week
- Supporting indicators
Gemini receives the already-computed result and helps users understand it, but it cannot change the underlying prediction.
We also implemented graceful fallback behavior when Gemini is unavailable, so the core forecast remains functional.
On top of that, we added Redis caching so repeated chatbot questions can be served without repeatedly consuming Gemini tokens.
What we learned
This project taught us that deploying a machine-learning model involves much more than training it.
We learned how to:
- Combine multiple real-world datasets
- Engineer time-based features without leaking future information
- Train and evaluate a next-week prediction model
- Export ML preprocessing and model artifacts in a portable format
- Serve machine-learning inference through FastAPI
- Connect a React frontend to a Python backend
- Visualize both weekly forecasts and day-by-day environmental data
- Integrate Gemini without allowing generative AI to control model predictions
- Use Redis and Docker to cache chatbot responses
- Design fallback behavior so optional AI features do not break the core application
- Coordinate development using Git branches, pull requests, and shared API/data contracts
One of the biggest lessons was that a reliable AI application needs clear boundaries between the systems that calculate, explain, display, and cache information.
What's next for Mosquito Watch
For future implementations, we would like to make Mosquito Watch more dynamic and continuously updated.
We want to automatically refresh mosquito-surveillance and weather data instead of relying on a fixed dataset, and validate the forecasting model across multiple mosquito seasons.
Future improvements could include:
- Automatically refreshed mosquito and weather data
- Improved day-by-day weather and mosquito visualizations
- Alerts when a ZIP code moves into a higher-risk category
- Borough and neighborhood comparisons
- More persistent Redis caching with expiration and cache invalidation
- A more capable chatbot for historical and forecast-related questions
- Additional years of surveillance data for model evaluation
- Geographic expansion beyond NYC
- Collaboration with public-health experts to validate how forecasts should be interpreted
Ultimately, we would like Mosquito Watch to become a continuously updated tool for exploring mosquito surveillance trends and understanding where elevated mosquito activity may be more likely in the coming week.
Log in or sign up for Devpost to join the conversation.