Jikkii commited on
Commit
038aba5
·
1 Parent(s): 96d425d

Using names instead of geoloc

Browse files
Files changed (2) hide show
  1. app.py +76 -127
  2. camptocamp_api.py +34 -63
app.py CHANGED
@@ -1,161 +1,110 @@
1
  import gradio as gr
2
  from camptocamp_api import CamptocampAPI
3
- from typing import Tuple, Optional
4
 
5
-
6
- # Instantiate the Camptocamp API wrapper
7
  c2c = CamptocampAPI(language="en")
8
 
9
-
10
- def get_recent_outings(
11
- west: float,
12
- south: float,
13
- east: float,
14
- north: float,
15
  start_date: Optional[str] = None,
16
  end_date: Optional[str] = None,
17
  activity: Optional[str] = None,
18
  limit: int = 10
19
  ) -> dict:
20
  """
21
- Get recent outings near a given location and date range.
22
-
23
- Args:
24
- west: Western longitude of the bounding box.
25
- south: Southern latitude of the bounding box.
26
- east: Eastern longitude of the bounding box.
27
- north: Northern latitude of the bounding box.
28
- start_date: Start of the date range (YYYY-MM-DD).
29
- end_date: End of the date range (YYYY-MM-DD).
30
- activity: Optional activity filter (e.g. "hiking", "rock_climbing").
31
- limit: Number of results to return.
32
-
33
- Returns:
34
- JSON dictionary of matching outings.
35
  """
36
- bbox = (west, south, east, north)
 
 
37
  date_range = (start_date, end_date) if start_date and end_date else None
38
  return c2c.get_recent_outings(bbox, date_range, activity, limit)
39
 
40
-
41
- def search_routes_by_activity(
42
- west: float,
43
- south: float,
44
- east: float,
45
- north: float,
46
  activity: str,
47
  limit: int = 10
48
  ) -> dict:
49
  """
50
- Search for climbing or hiking routes in a given region.
51
-
52
- Args:
53
- west: Western longitude of the bounding box.
54
- south: Southern latitude of the bounding box.
55
- east: Eastern longitude of the bounding box.
56
- north: Northern latitude of the bounding box.
57
- activity: Activity type (e.g. "rock_climbing", "hiking").
58
- limit: Number of routes to return.
59
-
60
- Returns:
61
- JSON dictionary of matching route metadata.
62
  """
63
- bbox = (west, south, east, north)
 
 
64
  return c2c.search_routes_by_activity(bbox, activity, limit)
65
 
66
-
67
  def get_route_details(route_id: int) -> dict:
68
  """
69
  Retrieve detailed route information by route ID.
70
-
71
- Args:
72
- route_id: Unique numeric ID of the route.
73
-
74
- Returns:
75
- JSON dictionary of route information including description, grade, etc.
76
  """
77
  return c2c.get_route_details(route_id)
78
 
79
-
80
- def search_waypoints(
81
- west: float,
82
- south: float,
83
- east: float,
84
- north: float,
85
- limit: int = 10
86
- ) -> dict:
87
  """
88
- Get known waypoints (e.g. huts, peaks, cols) in a given area.
89
-
90
- Args:
91
- west: Western longitude of the bounding box.
92
- south: Southern latitude of the bounding box.
93
- east: Eastern longitude of the bounding box.
94
- north: Northern latitude of the bounding box.
95
- limit: Maximum number of results.
96
-
97
- Returns:
98
- JSON dictionary of waypoints.
99
  """
100
- bbox = (west, south, east, north)
 
 
101
  return c2c.search_waypoints(bbox, limit)
102
 
103
-
104
- # ========== Gradio UI Layer ==========
105
-
106
- with gr.Blocks(title="Camptocamp API MCP") as demo:
107
- gr.Markdown("# 🏔️ Camptocamp API Interface")
108
-
109
- with gr.Tab("Recent Outings"):
110
- with gr.Row():
111
- west = gr.Number(label="West")
112
- south = gr.Number(label="South")
113
- east = gr.Number(label="East")
114
- north = gr.Number(label="North")
115
- with gr.Row():
116
- start_date = gr.Textbox(label="Start Date (YYYY-MM-DD)")
117
- end_date = gr.Textbox(label="End Date (YYYY-MM-DD)")
118
- activity = gr.Textbox(label="Activity (e.g. hiking, skiing)")
119
- limit = gr.Number(label="Limit", value=5)
120
- outings_output = gr.JSON()
121
- outings_btn = gr.Button("Get Outings")
122
- outings_btn.click(get_recent_outings,
123
- inputs=[west, south, east, north, start_date, end_date, activity, limit],
124
- outputs=outings_output)
125
-
126
- with gr.Tab("Routes"):
127
- with gr.Row():
128
- r_west = gr.Number(label="West")
129
- r_south = gr.Number(label="South")
130
- r_east = gr.Number(label="East")
131
- r_north = gr.Number(label="North")
132
- r_activity = gr.Textbox(label="Activity")
133
- r_limit = gr.Number(label="Limit", value=5)
134
- route_output = gr.JSON()
135
- route_btn = gr.Button("Search Routes")
136
- route_btn.click(search_routes_by_activity,
137
- inputs=[r_west, r_south, r_east, r_north, r_activity, r_limit],
138
- outputs=route_output)
139
-
140
- with gr.Tab("Route Details"):
141
- route_id_input = gr.Number(label="Route ID")
142
- route_details_output = gr.JSON()
143
- route_details_btn = gr.Button("Get Route Details")
144
- route_details_btn.click(get_route_details,
145
- inputs=[route_id_input],
146
- outputs=route_details_output)
147
-
148
- with gr.Tab("Waypoints"):
149
- with gr.Row():
150
- w_west = gr.Number(label="West")
151
- w_south = gr.Number(label="South")
152
- w_east = gr.Number(label="East")
153
- w_north = gr.Number(label="North")
154
- w_limit = gr.Number(label="Limit", value=5)
155
- waypoints_output = gr.JSON()
156
- waypoints_btn = gr.Button("Search Waypoints")
157
- waypoints_btn.click(search_waypoints,
158
- inputs=[w_west, w_south, w_east, w_north, w_limit],
159
- outputs=waypoints_output)
 
160
 
161
  demo.launch()
 
1
  import gradio as gr
2
  from camptocamp_api import CamptocampAPI
3
+ from typing import Optional
4
 
 
 
5
  c2c = CamptocampAPI(language="en")
6
 
7
+ def get_recent_outings_by_location(
8
+ location: str,
 
 
 
 
9
  start_date: Optional[str] = None,
10
  end_date: Optional[str] = None,
11
  activity: Optional[str] = None,
12
  limit: int = 10
13
  ) -> dict:
14
  """
15
+ Get recent outings for a location name using geocoded bounding box.
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  """
17
+ bbox = c2c.get_bbox_from_location(location)
18
+ if not bbox:
19
+ return {"error": f"Could not resolve bounding box for location: {location}"}
20
  date_range = (start_date, end_date) if start_date and end_date else None
21
  return c2c.get_recent_outings(bbox, date_range, activity, limit)
22
 
23
+ def search_routes_by_location(
24
+ location: str,
 
 
 
 
25
  activity: str,
26
  limit: int = 10
27
  ) -> dict:
28
  """
29
+ Search for routes near a named location.
 
 
 
 
 
 
 
 
 
 
 
30
  """
31
+ bbox = c2c.get_bbox_from_location(location)
32
+ if not bbox:
33
+ return {"error": f"Could not find bounding box for location: {location}"}
34
  return c2c.search_routes_by_activity(bbox, activity, limit)
35
 
 
36
  def get_route_details(route_id: int) -> dict:
37
  """
38
  Retrieve detailed route information by route ID.
 
 
 
 
 
 
39
  """
40
  return c2c.get_route_details(route_id)
41
 
42
+ def search_waypoints_by_location(location: str, limit: int = 10) -> dict:
 
 
 
 
 
 
 
43
  """
44
+ Get known waypoints (e.g. huts, peaks) in a given region.
 
 
 
 
 
 
 
 
 
 
45
  """
46
+ bbox = c2c.get_bbox_from_location(location)
47
+ if not bbox:
48
+ return {"error": f"No bounding box found for: {location}"}
49
  return c2c.search_waypoints(bbox, limit)
50
 
51
+ def lookup_bbox_from_location(location_name: str) -> Optional[dict]:
52
+ """
53
+ Look up the bounding box for a given place name.
54
+ """
55
+ bbox = c2c.get_bbox_from_location(location_name)
56
+ if not bbox:
57
+ return {"error": f"No bounding box found for: {location_name}"}
58
+ return {
59
+ "west": bbox[0],
60
+ "south": bbox[1],
61
+ "east": bbox[2],
62
+ "north": bbox[3]
63
+ }
64
+
65
+ with gr.Blocks(title="Camptocamp MCP Server") as demo:
66
+ gr.Markdown("# 🏔️ Camptocamp API via MCP")
67
+
68
+ with gr.Tab("📍 Recent Outings"):
69
+ loc = gr.Textbox(label="Location (e.g. Chamonix, La Grave)")
70
+ start = gr.Textbox(label="Start Date (YYYY-MM-DD)")
71
+ end = gr.Textbox(label="End Date (YYYY-MM-DD)")
72
+ act = gr.Textbox(label="Activity (e.g. hiking, climbing)")
73
+ limit = gr.Number(label="Result Limit", value=5)
74
+ out = gr.JSON()
75
+ gr.Button("Get Outings").click(get_recent_outings_by_location,
76
+ inputs=[loc, start, end, act, limit],
77
+ outputs=out)
78
+
79
+ with gr.Tab("🧗 Search Routes"):
80
+ rloc = gr.Textbox(label="Location (e.g. Alps)")
81
+ ract = gr.Textbox(label="Activity (e.g. rock_climbing)")
82
+ rlim = gr.Number(label="Result Limit", value=5)
83
+ rout = gr.JSON()
84
+ gr.Button("Search Routes").click(search_routes_by_location,
85
+ inputs=[rloc, ract, rlim],
86
+ outputs=rout)
87
+
88
+ with gr.Tab("📄 Route Details"):
89
+ rid = gr.Number(label="Route ID")
90
+ rdet = gr.JSON()
91
+ gr.Button("Get Route Details").click(get_route_details,
92
+ inputs=[rid],
93
+ outputs=rdet)
94
+
95
+ with gr.Tab("⛰ Waypoints"):
96
+ wloc = gr.Textbox(label="Location (e.g. Mont Blanc)")
97
+ wlim = gr.Number(label="Result Limit", value=5)
98
+ wout = gr.JSON()
99
+ gr.Button("Get Waypoints").click(search_waypoints_by_location,
100
+ inputs=[wloc, wlim],
101
+ outputs=wout)
102
+
103
+ with gr.Tab("🌍 Location → BBox"):
104
+ lstr = gr.Textbox(label="Location Name")
105
+ lout = gr.JSON()
106
+ gr.Button("Lookup BBox").click(lookup_bbox_from_location,
107
+ inputs=[lstr],
108
+ outputs=lout)
109
 
110
  demo.launch()
camptocamp_api.py CHANGED
@@ -11,28 +11,11 @@ class CamptocampAPI:
11
  BASE_URL = "https://api.camptocamp.org"
12
 
13
  def __init__(self, language: str = "en") -> None:
14
- """
15
- Initialize the API client.
16
-
17
- Args:
18
- language (str): Language code for results ('en', 'fr', etc.).
19
- """
20
  self.language = language
21
 
22
  def _request(self, endpoint: str, params: Dict[str, Any]) -> Dict[str, Any]:
23
- """
24
- Internal method to send a GET request to the Camptocamp API.
25
-
26
- Args:
27
- endpoint (str): API endpoint (e.g., "/outings").
28
- params (Dict): Dictionary of query parameters.
29
-
30
- Returns:
31
- Dict: Parsed JSON response.
32
- """
33
  params["pl"] = self.language
34
- url = f"{self.BASE_URL}{endpoint}"
35
- response = requests.get(url, params=params)
36
  response.raise_for_status()
37
  return response.json()
38
 
@@ -41,86 +24,74 @@ class CamptocampAPI:
41
  bbox: Tuple[float, float, float, float],
42
  date_range: Optional[Tuple[str, str]] = None,
43
  activity: Optional[str] = None,
44
- limit: int = 10,
45
  ) -> Dict[str, Any]:
46
- """
47
- Get recent outings around a specific location and date.
48
-
49
- Args:
50
- bbox (Tuple[float, float, float, float]): Bounding box as (west, south, east, north).
51
- date_range (Optional[Tuple[str, str]]): Date range (start, end) in YYYY-MM-DD format.
52
- activity (Optional[str]): Activity filter (e.g., "climbing", "hiking").
53
- limit (int): Max number of results.
54
-
55
- Returns:
56
- Dict: API response containing outings.
57
- """
58
  params = {
59
  "bbox": ",".join(map(str, bbox)),
60
  "limit": limit,
61
- "orderby": "-date",
62
  }
63
  if date_range:
64
  params["date"] = f"{date_range[0]},{date_range[1]}"
65
  if activity:
66
  params["act"] = activity
67
-
68
  return self._request("/outings", params)
69
 
70
  def search_routes_by_activity(
71
  self,
72
  bbox: Tuple[float, float, float, float],
73
  activity: str,
74
- limit: int = 10,
75
  ) -> Dict[str, Any]:
76
- """
77
- Search for routes in a given area and activity type.
78
-
79
- Args:
80
- bbox (Tuple[float, float, float, float]): Bounding box as (west, south, east, north).
81
- activity (str): Activity filter (e.g., "rock_climbing").
82
- limit (int): Max number of results.
83
-
84
- Returns:
85
- Dict: API response containing route data.
86
- """
87
  params = {
88
  "bbox": ",".join(map(str, bbox)),
89
  "act": activity,
90
  "limit": limit,
91
- "orderby": "-date",
92
  }
93
  return self._request("/routes", params)
94
 
95
  def get_route_details(self, route_id: int) -> Dict[str, Any]:
96
- """
97
- Retrieve full route information by its ID.
98
-
99
- Args:
100
- route_id (int): Camptocamp route ID.
101
-
102
- Returns:
103
- Dict: Full route document with metadata and description.
104
- """
105
  return self._request(f"/routes/{route_id}/{self.language}", {})
106
 
107
  def search_waypoints(
108
  self,
109
  bbox: Tuple[float, float, float, float],
110
- limit: int = 10,
111
  ) -> Dict[str, Any]:
 
 
 
 
 
 
 
 
112
  """
113
- Get waypoints (e.g., huts, summits) within a given area.
114
 
115
  Args:
116
- bbox (Tuple[float, float, float, float]): Bounding box as (west, south, east, north).
117
- limit (int): Max number of results.
118
 
119
  Returns:
120
- Dict: API response with waypoint data.
121
  """
 
122
  params = {
123
- "bbox": ",".join(map(str, bbox)),
124
- "limit": limit,
 
125
  }
126
- return self._request("/waypoints", params)
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  BASE_URL = "https://api.camptocamp.org"
12
 
13
  def __init__(self, language: str = "en") -> None:
 
 
 
 
 
 
14
  self.language = language
15
 
16
  def _request(self, endpoint: str, params: Dict[str, Any]) -> Dict[str, Any]:
 
 
 
 
 
 
 
 
 
 
17
  params["pl"] = self.language
18
+ response = requests.get(f"{self.BASE_URL}{endpoint}", params=params)
 
19
  response.raise_for_status()
20
  return response.json()
21
 
 
24
  bbox: Tuple[float, float, float, float],
25
  date_range: Optional[Tuple[str, str]] = None,
26
  activity: Optional[str] = None,
27
+ limit: int = 10
28
  ) -> Dict[str, Any]:
 
 
 
 
 
 
 
 
 
 
 
 
29
  params = {
30
  "bbox": ",".join(map(str, bbox)),
31
  "limit": limit,
32
+ "orderby": "-date"
33
  }
34
  if date_range:
35
  params["date"] = f"{date_range[0]},{date_range[1]}"
36
  if activity:
37
  params["act"] = activity
 
38
  return self._request("/outings", params)
39
 
40
  def search_routes_by_activity(
41
  self,
42
  bbox: Tuple[float, float, float, float],
43
  activity: str,
44
+ limit: int = 10
45
  ) -> Dict[str, Any]:
 
 
 
 
 
 
 
 
 
 
 
46
  params = {
47
  "bbox": ",".join(map(str, bbox)),
48
  "act": activity,
49
  "limit": limit,
50
+ "orderby": "-date"
51
  }
52
  return self._request("/routes", params)
53
 
54
  def get_route_details(self, route_id: int) -> Dict[str, Any]:
 
 
 
 
 
 
 
 
 
55
  return self._request(f"/routes/{route_id}/{self.language}", {})
56
 
57
  def search_waypoints(
58
  self,
59
  bbox: Tuple[float, float, float, float],
60
+ limit: int = 10
61
  ) -> Dict[str, Any]:
62
+ params = {
63
+ "bbox": ",".join(map(str, bbox)),
64
+ "limit": limit
65
+ }
66
+ return self._request("/waypoints", params)
67
+
68
+ @staticmethod
69
+ def get_bbox_from_location(query: str) -> Optional[Tuple[float, float, float, float]]:
70
  """
71
+ Geocode a location string and return a bounding box.
72
 
73
  Args:
74
+ query: Name of the place or location (e.g., "Chamonix, France").
 
75
 
76
  Returns:
77
+ Bounding box as (west, south, east, north) or None if not found.
78
  """
79
+ url = "https://nominatim.openstreetmap.org/search"
80
  params = {
81
+ "q": query,
82
+ "format": "json",
83
+ "limit": 1
84
  }
85
+ headers = {"User-Agent": "camptocamp-api-wrapper"}
86
+ response = requests.get(url, params=params, headers=headers)
87
+ response.raise_for_status()
88
+ results = response.json()
89
+ if not results:
90
+ return None
91
+ bbox = results[0]["boundingbox"] # ['south', 'north', 'west', 'east']
92
+ return (
93
+ float(bbox[2]), # west
94
+ float(bbox[0]), # south
95
+ float(bbox[3]), # east
96
+ float(bbox[1]) # north
97
+ )