EV-Charging-Station-Predictor Turns City Maps Into Placement Math

A geospatial ML pipeline that reads OpenStreetMap like a satellite, learns from negative samples, and scores where EV chargers should go.

8 min read • View on GitHub • More from Divyak-07

A bird’s-eye city block is rendered like a layered signal diagram. Roads, buildings, green space, and amenity markers feed into a central scoring engine, showing how map texture becomes machine-readable input for charger placement.
The repo treats OpenStreetMap as a stack of spatial signals, not just background map data.
Key Takeaways

Most EV prediction projects ask a narrow question: how much traffic will a charger get, or when will it be used? This repo asks a different one. Where should the charger go in the first place? That shift matters because site suitability is a spatial problem before it is a forecasting problem.

The city is the model

The central move is to treat a city like a machine-readable signal stack. Roads, buildings, land use, and nearby amenities are not treated as decoration. They become features. In this repo, OpenStreetMap is less a map than a dense urban sensor.

A drawn bounding box triggers data fetch, feature extraction, negative sampling, prediction, and a heatmap response.

That framing is what makes the project feel unusual. It does not start with a dataset of charger sessions and then fit a forecast. It starts with the urban fabric itself and asks which places look charger-friendly before any utilization history enters the picture.

How streets become features

The core pipeline lives in ev_ml_predictor.py. The code parses OSM data around a site, then compresses what it finds into a 23-feature vector inside a 500 meter radius. That vector includes counts and normalized signals from tags such as highway, amenity, landuse, and building.

QUERY_RADIUS_M = 500

features = extract_features_from_overpass(
    osm_data,
    center_lat=lat,
    center_lon=lon,
    radius_m=QUERY_RADIUS_M,
)

# Features include road counts, node density,
# land-use mix, building density, and amenities.
X = np.array([features])
prediction = model.predict_proba(X)[0, 1]

The important part is not the model input shape. It is the geometry-to-table conversion. Spatial ML fails when the map stays pictorial. This repo makes the map numeric, then lets tree-based models do what they are good at: combining many weak signals into a sharper decision.

A real charger point sits beside several intentionally bad candidate points scattered into low-amenity terrain, cul-de-sacs, and isolated parcels. The contrast explains how the model learns not only where chargers exist, but where they do not belong.
Negative sampling teaches the classifier the boundary between plausible and implausible charger sites.

Why negative samples make the model smarter

The hidden strength of the dataset is negative sampling. The pipeline does not only collect coordinates where chargers already exist. It also manufactures nearby places that do not have chargers. That gives the classifier something essential: examples of urban context that looks similar on the surface but should score lower.

ApproachTraining dataWhat it learnsMain weakness
Positive-only site listsExisting charger locationsWhat charger areas look likeLearns one class, not a boundary
Naive GIS scoringRoads and nearby POIsA hand-tuned suitability heuristicHard to calibrate and easy to overfit
This repo’s hybrid datasetChargers plus nearby negative samplesWhere charger sites belong and where they do notDepends on thoughtful sampling and feature quality

That distinction is easy to miss and easy to underestimate. A model trained only on charger locations can become a pattern matcher for density. A model trained on positive and negative sites can begin to learn placement.

Heuristics before ML, not after it

The repo does not pretend the model is the only source of judgment. In ev_campus_analyzer.py, heuristic scoring comes first. The code classifies urban context, assigns local suitability signals, and uses that baseline to structure the problem before the ML layer refines it.

That is a mature design choice. It avoids the usual false binary between rules and ML. In this repo, heuristics are not a fallback. They are a scaffold.

The dashboard turns the model into a planning tool

The product moment appears in the Flask and Leaflet layer. A user draws a bounding box, the backend fetches fresh OSM data, the model scores the area, and the frontend renders the result as a GeoJSON heatmap. The key endpoint, /api/predict-bbox, turns the whole pipeline into something interactive.

That matters because a placement model is only useful if someone can interrogate it. The dashboard does not just display an answer. It lets a planner ask a local question, then see how the system reasons across the selected area.

How it differs from ordinary EV prediction projects

DimensionTypical EV prediction projectThis repo
Prediction targetUsage, occupancy, or loadSite suitability
Core dataHistorical station activityGeospatial context from OpenStreetMap and OpenChargeMap
Feature styleTemporal or network-basedSpatial and land-use based
User interactionOften offline notebooksDraw-a-box web workflow
StrengthForecasting demandSelecting where infrastructure should go
WeaknessLess useful for planning siting decisionsLess focused on session-level forecasting

The difference is philosophical as much as technical. Many projects forecast what happens after a station exists. This one is about deciding whether the station deserves to exist there at all.

Why the repo feels more mature than a tutorial

The supporting pieces matter. Optuna appears in the model tuning flow. There is benchmarking code, LaTeX reporting, Docker packaging, Gunicorn deployment, and a cloud-ready configuration. Those are not decorative extras. They signal a repo built to be used, reproduced, and compared.

The strongest open-source signal here is compositional: data fetching, feature engineering, modeling, reporting, and deployment are all present in one place. That makes the repo feel like a research artifact with a product path, not a notebook that escaped into version control.