brunoapj commited on
Commit
097a6f2
·
verified ·
1 Parent(s): 8fe89dd

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +230 -0
  2. requiremets.txt +2 -0
app.py ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # lugar_3.py
2
+ # Gradio 6.2.0
3
+ # Layout FULL PAGE (sem margens laterais)
4
+ # ✔ Busca por nome e coordenadas
5
+ # ✔ Mapa Leaflet
6
+ # ✔ Google Street View (fallback automático)
7
+ # ✔ Apple Maps
8
+
9
+ import re
10
+ import html
11
+ import requests
12
+ import gradio as gr
13
+
14
+
15
+ # ----------------------------
16
+ # Parsing / Geocoding
17
+ # ----------------------------
18
+ _COORD_RE = re.compile(
19
+ r"""
20
+ ^\s*
21
+ (?:lat\s*[:=]?\s*)?(-?\d+(?:\.\d+)?)
22
+ (?:\s*[,;]\s*|\s+)
23
+ (?:lon|lng|long)?\s*[:=]?\s*(-?\d+(?:\.\d+)?)
24
+ \s*$
25
+ """,
26
+ re.IGNORECASE | re.VERBOSE
27
+ )
28
+
29
+
30
+ def parse_coords(text: str):
31
+ if not text:
32
+ return None
33
+ m = _COORD_RE.match(text.strip())
34
+ if not m:
35
+ return None
36
+
37
+ lat = float(m.group(1))
38
+ lon = float(m.group(2))
39
+
40
+ if not (-90 <= lat <= 90 and -180 <= lon <= 180):
41
+ raise ValueError("Coordenadas fora do intervalo válido.")
42
+
43
+ return lat, lon
44
+
45
+
46
+ def geocode_by_name(query: str):
47
+ url = "https://nominatim.openstreetmap.org/search"
48
+ params = {"q": query, "format": "jsonv2", "limit": 1}
49
+ headers = {"User-Agent": "GradioMapFullPage"}
50
+
51
+ r = requests.get(url, params=params, headers=headers, timeout=20)
52
+ r.raise_for_status()
53
+ data = r.json()
54
+
55
+ if not data:
56
+ raise ValueError(f"Local não encontrado: {query}")
57
+
58
+ lat = float(data[0]["lat"])
59
+ lon = float(data[0]["lon"])
60
+ name = data[0].get("display_name", query)
61
+ return lat, lon, name
62
+
63
+
64
+ def resolve_location(mode, place, lat, lon):
65
+ if mode == "coords":
66
+ return float(lat), float(lon), f"{lat:.6f}, {lon:.6f}"
67
+
68
+ if mode == "auto":
69
+ parsed = parse_coords(place)
70
+ if parsed:
71
+ la, lo = parsed
72
+ return la, lo, f"{la:.6f}, {lo:.6f}"
73
+
74
+ return geocode_by_name(place)
75
+
76
+
77
+ # ----------------------------
78
+ # Map HTML (srcdoc)
79
+ # ----------------------------
80
+ def build_srcdoc(lat, lon, zoom, basemap, title):
81
+ sv0 = f"https://www.google.com/maps/@?api=1&map_action=pano&viewpoint={lat:.7f},{lon:.7f}"
82
+ a0 = f"https://maps.apple.com/place?coordinate={lat:.7f},{lon:.7f}"
83
+
84
+ doc = f"""
85
+ <!doctype html>
86
+ <html>
87
+ <head>
88
+ <meta charset="utf-8"/>
89
+ <meta name="viewport" content="width=device-width, initial-scale=1"/>
90
+
91
+ <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/>
92
+ <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
93
+
94
+ <style>
95
+ html, body {{
96
+ margin:0;
97
+ padding:0;
98
+ width:100%;
99
+ }}
100
+
101
+ .wrap {{
102
+ width:100%;
103
+ padding:12px;
104
+ }}
105
+
106
+ #map {{
107
+ width:100%;
108
+ height:70vh;
109
+ border-radius:14px;
110
+ border:1px solid #e5e7eb;
111
+ }}
112
+
113
+ .panel {{
114
+ margin-top:10px;
115
+ padding:12px;
116
+ border:1px solid #e5e7eb;
117
+ border-radius:14px;
118
+ }}
119
+
120
+ .btns {{
121
+ display:flex;
122
+ gap:10px;
123
+ margin-top:8px;
124
+ }}
125
+
126
+ .btn {{
127
+ padding:10px 14px;
128
+ border-radius:10px;
129
+ font-weight:700;
130
+ text-decoration:none;
131
+ color:white;
132
+ }}
133
+
134
+ .btn.google {{ background:#1a73e8; }}
135
+ .btn.apple {{ background:#000; }}
136
+
137
+ code {{
138
+ background:#f3f4f6;
139
+ padding:3px 8px;
140
+ border-radius:8px;
141
+ }}
142
+ </style>
143
+ </head>
144
+
145
+ <body>
146
+ <div class="wrap">
147
+ <h3>{html.escape(title)}</h3>
148
+ <div id="map"></div>
149
+
150
+ <div class="panel">
151
+ <div><b>Coordenadas:</b> <code id="coords">{lat:.7f}, {lon:.7f}</code></div>
152
+
153
+ <div class="btns">
154
+ <a id="streetLink" class="btn google" href="{sv0}" target="_blank">Google Street View</a>
155
+ <a id="appleLink" class="btn apple" href="{a0}" target="_blank">Apple Maps</a>
156
+ </div>
157
+ </div>
158
+ </div>
159
+
160
+ <script>
161
+ const map = L.map('map').setView([{lat:.7f}, {lon:.7f}], {zoom});
162
+
163
+ const bases = {{
164
+ "OSM": L.tileLayer('https://{{s}}.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png'),
165
+ "Esri Satellite": L.tileLayer(
166
+ 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{{z}}/{{y}}/{{x}}')
167
+ }};
168
+
169
+ (bases["{basemap}"] || bases["OSM"]).addTo(map);
170
+ L.control.layers(bases).addTo(map);
171
+
172
+ let marker = L.marker([{lat:.7f}, {lon:.7f}]).addTo(map);
173
+
174
+ function update(lat, lon) {{
175
+ marker.setLatLng([lat, lon]);
176
+ document.getElementById("coords").textContent =
177
+ lat.toFixed(7) + ", " + lon.toFixed(7);
178
+
179
+ document.getElementById("streetLink").href =
180
+ "https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=" + lat + "," + lon;
181
+
182
+ document.getElementById("appleLink").href =
183
+ "https://maps.apple.com/place?coordinate=" + lat + "," + lon;
184
+ }}
185
+
186
+ map.on("click", e => update(e.latlng.lat, e.latlng.lng));
187
+ </script>
188
+ </body>
189
+ </html>
190
+ """
191
+ return html.escape(doc, quote=True)
192
+
193
+
194
+ def run(mode, place, lat, lon, zoom, basemap):
195
+ lat0, lon0, name = resolve_location(mode, place, lat, lon)
196
+ srcdoc = build_srcdoc(lat0, lon0, zoom, basemap, f"📍 {name}")
197
+ iframe = f'<iframe style="width:100vw; height:85vh; border:0;" srcdoc="{srcdoc}"></iframe>'
198
+ return f"OK — {name}", iframe
199
+
200
+
201
+ # ----------------------------
202
+ # UI (FULL PAGE)
203
+ # ----------------------------
204
+ CSS = """
205
+ .gradio-container {
206
+ max-width: 100% !important;
207
+ padding-left: 0 !important;
208
+ padding-right: 0 !important;
209
+ }
210
+ """
211
+
212
+ with gr.Blocks(css=CSS, title="Mapa Full Page") as demo:
213
+ with gr.Row():
214
+ mode = gr.Radio(["auto", "nome", "coords"], value="auto", label="Modo")
215
+
216
+ place = gr.Textbox(label="Local ou coordenadas", value="Lisboa")
217
+ lat_in = gr.Number(label="Latitude", value=38.7223, precision=7)
218
+ lon_in = gr.Number(label="Longitude", value=-9.1393, precision=7)
219
+
220
+ basemap = gr.Dropdown(["Esri Satellite", "OSM"], value="Esri Satellite", label="Base map")
221
+ zoom = gr.Slider(3, 20, value=16, label="Zoom")
222
+
223
+ btn = gr.Button("Mostrar", variant="primary")
224
+ status = gr.Textbox(label="Status", interactive=False)
225
+ view = gr.HTML()
226
+
227
+ btn.click(run, [mode, place, lat_in, lon_in, zoom, basemap], [status, view])
228
+ demo.load(lambda: run("auto", "Lisboa", 38.7223, -9.1393, 12, "Esri Satellite"), outputs=[status, view])
229
+
230
+ demo.launch()
requiremets.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ gradio>=4.0.0
2
+ requests>=2.25.0