Skip to content

Commit b4d74b6

Browse files
author
Alfiya Tarasenko
committed
Add Route Planner example
1 parent 1b66697 commit b4d74b6

4 files changed

Lines changed: 1422 additions & 0 deletions

File tree

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,24 @@ This example demonstrates how to fetch **places data** from the Geoapify Places
146146
- [Asyncio](https://docs.python.org/3/library/asyncio.html), [Aiohttp](https://docs.aiohttp.org/)
147147

148148

149+
### **8. Python: [Route Planner Result Processor](https://github.com/geoapify/maps-api-code-samples/tree/main/python/route-planner)**
150+
151+
#### **Description:**
152+
This example demonstrates how to process a request to the **Geoapify Route Planner API**, generate structured outputs for each agent, and visualize their assigned routes.
153+
154+
#### **Features:**
155+
- Accepts a JSON request compatible with the Route Planner API.
156+
- Calls the **Route Planner API** and retrieves planned jobs and waypoints.
157+
- Creates **individual folders** for each agent:
158+
- Saves `plan.json` with job and route details.
159+
- Renders a **route map** using **Folium** and the **Routing API**.
160+
- Outputs an `issues.json` file listing any unassigned or problematic jobs.
161+
162+
#### **APIs Used:**
163+
- [Geoapify Route Planner API](https://www.geoapify.com/route-planner/)
164+
- [Geoapify Routing API](https://www.geoapify.com/routing-api/)
165+
- [Folium Library](https://python-visualization.github.io/folium/)
166+
149167
## Upcoming Code Samples
150168

151169
We plan to expand this repository with code samples in various programming languages, demonstrating different Geoapify APIs, including:

python/route-planner/README.md

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
# Route Planner Result Processor Example
2+
3+
This project demonstrates how to process a Route Planner API request using the **[Geoapify Route Planner API]((https://www.geoapify.com/route-planner-api/))**, generate structured outputs per agent, and visualize the assigned routes using **Folium** and [Routing API](https://www.geoapify.com/routing-api/).
4+
5+
## Requirements
6+
7+
Ensure you have the following installed:
8+
9+
1. Python 3.11 or higher
10+
2. pip (Python package manager)
11+
12+
## Setup Instructions
13+
14+
### 1. Clone the Repository
15+
16+
```bash
17+
git clone https://gitlab.com/geoapify-externals/code-samples
18+
cd code-samples/python/route_planner_example
19+
```
20+
21+
### 2. Create a Virtual Environment (Optional)
22+
23+
```bash
24+
python -m venv env
25+
source env/bin/activate # On Windows: env\Scripts\activate
26+
```
27+
28+
### 3. Install Dependencies
29+
30+
```bash
31+
pip install folium requests
32+
```
33+
34+
## Running the Example
35+
36+
```bash
37+
cd route-planner
38+
python route_planner.py --api_key YOUR_API_KEY --input request.json --output results/
39+
```
40+
41+
## Command-line Arguments
42+
43+
- `--api_key` (required): Geoapify API key.
44+
- `--input` (required): Path to the input JSON file. That represents a request for Route Planner API. You can generate examples of request with our [Playground](https://apidocs.geoapify.com/playground/route-planner/).
45+
- `--output` (required): Output directory to store results.
46+
47+
48+
## What It Does
49+
50+
- **Reads** a Route Planner API request (input JSON).
51+
- **Sends** the request to the Geoapify Route Planner API.
52+
- **Generates** structured results for each agent:
53+
- Saves `plan.json` with assigned waypoints and actions.
54+
- Creates a route **preview map (`map.html`)** using the **Routing API** and **Folium**.
55+
- **Outputs** an `issues.json` file listing any reported issues (e.g., unassigned jobs).
56+
57+
58+
## Example Output Structure
59+
60+
```
61+
results/
62+
│── issues.json
63+
│── agent_1/
64+
│ ├── plan.json
65+
│ ├── map.html
66+
│── agent_2/
67+
│ ├── plan.json
68+
│ ├── map.html
69+
...
70+
```
71+
72+
## Code Explanation
73+
This Python script automates the processing of a Route Planner API request using Geoapify's services, and generates detailed structured outputs for each agent.
74+
It is designed to work with JSON files formatted according to the Geoapify Route Planner API specifications.
75+
76+
The key features of the script include:
77+
* Sending the input JSON request to the Route Planner API and handling the response.
78+
* Extracting and saving individual agent plans in separate folders.
79+
* Creating interactive maps visualizing each agent’s optimized route using Folium.
80+
* Generating an issues report file listing any problems detected during the optimization (e.g., unassigned jobs).
81+
82+
83+
84+
Perfect! Let’s go function-by-function, starting with the `main()` function, then moving on to `extract_route_plans()` and its subfunctions, followed by `save_agent_plan()` and `save_issues_report()`.
85+
86+
---
87+
88+
### 1. `main()`
89+
90+
```python
91+
def main():
92+
# Parse cli arguments
93+
args = parse_arguments()
94+
95+
# Read and validate input JSON
96+
request_data = read_request_file(args.input)
97+
98+
# Call Geoapify Route Planner API to extract route plans
99+
try:
100+
response_data = extract_route_plans(args.api_key, request_data)
101+
except requests.exceptions.RequestException as e:
102+
print(f"API request failed: {e}")
103+
return
104+
105+
# Extract geojson properties as agents plans
106+
agents = response_data.get('features', [])
107+
# Extract issues occurred during the planning process
108+
issues = response_data['properties'].get('issues', {})
109+
110+
# For every agent plan save data and generate map with optimized route
111+
for agent_data in agents:
112+
save_agent_plan(args.api_key, agent_data['properties'], args.output)
113+
114+
save_issues_report(issues, args.output)
115+
```
116+
117+
#### What it does:
118+
- This is the main entry point of the script.
119+
- It parses command-line arguments, loads the input JSON request, sends it to the Route Planner API, and handles the response.
120+
- For each agent in the result, it creates a folder with:
121+
- A `plan.json` containing routing info.
122+
- A `map.html` visualizing the agent’s route.
123+
- It also saves `issues.json` summarizing any problems returned by the API.
124+
125+
### 2. `extract_route_plans(api_key, request_data)`
126+
127+
```python
128+
def extract_route_plans(api_key, request_data):
129+
response = requests.post(ROUTE_PLANNER_URL,
130+
params={'apiKey': api_key},
131+
json=request_data)
132+
response.raise_for_status()
133+
return response.json()
134+
```
135+
136+
#### What it does:
137+
- Sends a `POST` request to Geoapify's **Route Planner API**, using the provided request body.
138+
- Attaches the API key as a query parameter.
139+
- Raises an exception if the request fails (e.g., due to a network issue or invalid request).
140+
- Returns the parsed JSON response on success.
141+
142+
### 3. `save_agent_plan(api_key, agent_data, output_dir)`
143+
144+
```python
145+
def save_agent_plan(api_key, agent_data, output_dir):
146+
# Create dir that contains agent index in name
147+
agent_dir = os.path.join(output_dir, f'agent_{agent_data['agent_index']}')
148+
os.makedirs(agent_dir, exist_ok=True)
149+
150+
# Save plan.json
151+
plan_path = os.path.join(agent_dir, 'plan.json')
152+
with open(plan_path, 'w') as file:
153+
json.dump(agent_data, file)
154+
155+
156+
map_path = os.path.join(agent_dir, 'map.html')
157+
coordinates = [wp['location'] for wp in agent_data['waypoints']]
158+
try:
159+
# Generate route by Routing API
160+
route = get_route(api_key, coordinates)
161+
except requests.exceptions.RequestException as e:
162+
print(f'Cannot get route for agent {agent_data['agent_index']}: {e}')
163+
return
164+
# Generate map.html
165+
generate_map(route, map_path, coordinates, api_key)
166+
```
167+
168+
#### What it does:
169+
- Creates a folder for each agent (e.g., `agent_0`, `agent_1`).
170+
- Saves a JSON file (`plan.json`) with the agent’s route plan, actions, and assigned jobs.
171+
- Extracts waypoints from the agent's data and calls the **Routing API** to retrieve a navigable route.
172+
- Generates a Folium map (`map.html`) with markers and polylines visualizing the route.
173+
174+
175+
### 4. `get_route(api_key, waypoints)`
176+
177+
```python
178+
def get_route(api_key, waypoints):
179+
waypoints_str = '|'.join([f"lonlat:{lon},{lat}" for lon, lat in waypoints])
180+
url = ROUTING_URL.format(waypoints_str=waypoints_str)
181+
182+
response = requests.get(url, params={
183+
'waypoints': waypoints_str,
184+
'mode': 'drive',
185+
'apiKey': api_key
186+
})
187+
response.raise_for_status()
188+
return response.json()
189+
```
190+
191+
This function sends a **request to the Geoapify Routing API** to retrieve a route (polyline) based on a given list of waypoints.
192+
193+
- **Inputs**:
194+
- `api_key`: Your Geoapify API key for authentication.
195+
- `waypoints`: A list of `[longitude, latitude]` pairs representing the route points.
196+
197+
- **What it does**:
198+
1. Converts the list of waypoints into the API-expected string format:
199+
```
200+
lonlat:lon1,lat1|lonlat:lon2,lat2|...
201+
```
202+
2. Sends a **GET** request to the Routing API using the waypoints, `drive` mode, and your API key.
203+
3. If the API call succeeds, returns the route as a GeoJSON object.
204+
4. If the call fails, raises an exception to be caught in the higher-level function.
205+
206+
- **Notes**:
207+
- Currently, the `mode` is hardcoded as `drive`, but it can be extended easily.
208+
- Uses `raise_for_status()` for clean error handling.
209+
210+
211+
### 5. `generate_map(route_data, output_map, waypoints, api_key)`
212+
213+
```python
214+
def generate_map(route_data, output_map, waypoints, api_key):
215+
m = folium.Map(location=[0, 0], zoom_start=13)
216+
route = folium.GeoJson(route_data, style_function=lambda f: {
217+
'color': 'red',
218+
'weight': 7,
219+
'opacity': 0.8
220+
})
221+
route.add_to(m)
222+
m.fit_bounds(route.get_bounds())
223+
# Render markers that present every waypoint of route
224+
for num, coords in enumerate(waypoints):
225+
make_icon_marker(num + 1, coords, api_key).add_to(m)
226+
227+
m.save(output_map)
228+
```
229+
230+
This function **creates an interactive HTML map** using **Folium** that displays the calculated route and numbered waypoint markers.
231+
232+
- **Inputs**:
233+
- `route_data`: The GeoJSON route returned by the Routing API.
234+
- `output_map`: The filename to save the generated HTML map.
235+
- `waypoints`: List of `[lon, lat]` points to mark along the route.
236+
- `api_key`: Used if custom markers (via Geoapify Icon API) are added.
237+
238+
- **What it does**:
239+
1. Initializes a **Folium Map** centered at `[0,0]` (but adjusted later).
240+
2. Adds the route as a **red polyline** using `folium.GeoJson`.
241+
3. Adjusts the viewport automatically with `fit_bounds()` to show the full route.
242+
4. Adds **custom numbered markers** for each waypoint, calling `make_icon_marker()`.
243+
244+
- **Notes**:
245+
- The map has a nice zoom and line style for clarity.
246+
- Each waypoint is clickable with a styled marker.
247+
248+
### 6. `make_icon_marker(label, coords, api_key)`
249+
250+
```python
251+
def make_icon_marker(label, coords, api_key) -> folium.Marker:
252+
# Create custom marker from icon API
253+
marker_url = 'https://api.geoapify.com/v1/icon/?type=circle&color=red&size=large&text={label}&noShadow&noWhiteCircle&scaleFactor=2&apiKey={api_key}'
254+
icon = folium.CustomIcon(marker_url.format(api_key=api_key, label=label),
255+
icon_size=(31, 31),
256+
icon_anchor=(15, 15),
257+
popup_anchor=(0, -42))
258+
return folium.Marker(location=coords[::-1], icon=icon)
259+
260+
261+
def save_issues_report(issues, output_dir):
262+
issues_path = os.path.join(output_dir, 'issues.json')
263+
with open(issues_path, 'w') as file:
264+
json.dump(issues, file)
265+
```
266+
267+
This helper function **creates a custom Folium marker** with a **number or label** using the **Geoapify Map Marker API**.
268+
269+
- **Inputs**:
270+
- `label`: The number or text displayed inside the marker.
271+
- `coords`: The `[longitude, latitude]` position where the marker should be placed.
272+
- `api_key`: Geoapify API key needed to fetch the marker icon.
273+
274+
- **What it does**:
275+
1. Builds a URL to request a **red circular marker** with the given label from the Geoapify Map Marker API.
276+
2. Creates a `folium.CustomIcon` using that URL.
277+
3. Returns a **Folium Marker** positioned at the provided coordinates, using the custom icon.
278+
279+
- **Notes**:
280+
- Automatically flips coordinates (`coords[::-1]`) because Folium expects `[lat, lon]` order.
281+
- Marker size, anchor, and popup settings are optimized for clean display.
282+
283+
284+
### 7. `save_issues_report(issues, output_dir)`
285+
286+
```python
287+
def save_issues_report(issues, output_dir):
288+
issues_path = os.path.join(output_dir, 'issues.json')
289+
with open(issues_path, 'w') as file:
290+
json.dump(issues, file)
291+
```
292+
293+
#### What it does:
294+
- Writes a simple `issues.json` file containing problems reported by the Route Planner API.
295+
- These might include unassigned jobs, infeasible shipments, or routing errors.
296+
297+
## Related Links
298+
299+
- [Geoapify Route Planner API](https://www.geoapify.com/route-planner-api/)
300+
- [Geoapify Routing API](https://www.geoapify.com/routing-api/)
301+
- [Folium](https://python-visualization.github.io/folium/)
302+
303+
304+
## License
305+
306+
This example is provided under the [MIT License](../../LICENSE.md).
307+

0 commit comments

Comments
 (0)