+
    #j`                        R t ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	t^ RI
tRtRtRtRtRR.t]^ ,          tRtR	tR
t^tRt/ RR^bRR_bRR`bRRaRb.bRRcbRRdbRRebRRfbRRgbRRhbRRibRRjbRRkbRRlbRRmbRRnbRRob/ RRpbRRqbRRrbR RsbR!RtbR"RubR#RvbR$RwbR%RxbR&RybR'RzbR(R{bR)R|bR*R}bR+R~bR,RbR-RbCR.RR/RR0RR1RR2RR3RR4RR5RR6RR7RR8RR9R/CtR2R:R3R;R4R</t]! ]P3                  4       4      tR= tR>R>R?R@RARB/tRC tRRD ltR]RE3RF ltR]RE3RG lt ]3RH lt!RI t"RJ t#RRK lt$RL t%RM t&RRN lt'RRO lt(RRP lt)RQ t*RR t+RS t,RT t-RU t.RV t/RW t0RX t1RY t2RZ t3R[ t4R\ t5]6R]8X  d
   ]5! 4        R# R# )ah  
maps_client.py - CLI tool for maps, geocoding, routing, POI search, and more.
Uses only Python stdlib. Data from OpenStreetMap/Nominatim, Overpass API, OSRM,
and TimeAPI.io.

Commands:
  search     - Geocode a place name to coordinates
  reverse    - Reverse geocode coordinates to an address
  nearby     - Find nearby POIs by category
  distance   - Road distance and travel time between two places
  directions - Turn-by-turn directions between two places
  timezone   - Timezone info for coordinates
  bbox       - Find POIs within a bounding box
  area       - Get bounding box and area info for a named place
Nz*HermesAgent/1.0 (contact: hermes@agent.ai)zOpenStreetMap/Nominatimz*https://nominatim.openstreetmap.org/searchz+https://nominatim.openstreetmap.org/reversez'https://overpass-api.de/api/interpreterz-https://overpass.kumi.systems/api/interpreterz(https://router.project-osrm.org/route/v1z*https://timeapi.io/api/timezone/coordinateg      ?g       @
restaurantcafebarbakeryconvenience_storehospitalpharmacydentistdoctor
veterinaryhotelguest_house	camp_sitesupermarketbookshoplaundryatmbankgas_stationparkingairporttrain_stationbus_stoptaxicar_wash
car_rentalbicycle_rentalmuseumcinematheatre	nightclubzooschool
universitylibrarypolicefire_stationpost_officechurchmosque	synagogueparkgymswimming_pool
playgroundstadium	christianmuslimjewishc                d    \         V ,          p\        V\        4      '       d   \        V4      # V.# )a  Return the CATEGORY_TAGS entry as a list of (key, value) pairs.

Most categories map to a single (tag_key, tag_val) tuple, but some
(e.g. ``bakery``) are tagged under more than one OSM key and are
represented as a list of tuples. Normalise both forms to a list.
)CATEGORY_TAGS
isinstancelist)categoryentrys   & f/opt/hermes-venv/lib/python3.14/site-packages/../../../skills/productivity/maps/scripts/maps_client.py	_tags_forr:      s,     (#E%E{7N    drivingwalkingfootcyclingbikec                J    \        \        P                  ! V ^RR7      4       R# )z,Print data as pretty-printed JSON to stdout.F)indentensure_asciiN)printjsondumps)datas   &r9   
print_jsonrH      s    	$**T!%
89r;   c                P    \        RV RR/4       \        P                  ! V4       R# )z'Print an error result as JSON and exit.errorstatusN)rH   sysexit)messagecodes   &&r9   
error_exitrP      s    (G45HHTNr;   Fc                   V'       d.   V R,           \         P                  P                  V4      ,           p \         P                  P	                  V R\
        /R7      pRp\        ^V^,           4       Fm  p \         P                  P                  V^R7      ;_uu_ 4       pVP                  4       P                  R4      p\        P                  ! V4      uuRRR4       u # 	  RV RV 2p
V'       d   \'        V
4      h\)        V
4       R#   + '       g   i     K  ; i  \         P                  P                   d   p	RT	P                   RT	P                   R	T  2pT	P                  R9   d)   \         P"                  ! \$        T,          4        Rp	?	EK$  T'       d   \'        T4      h\)        T4        Rp	?	EKI  Rp	?	i\         P                  P*                   d=   p	R
T	P                   2p\         P"                  ! \$        T,          4        Rp	?	EK  Rp	?	i\        P,                   d3   p	RT	 2p\         P"                  ! \$        T,          4        Rp	?	EK  Rp	?	ii ; i)z
Perform an HTTP GET request, returning parsed JSON.
Adds the required User-Agent header. Retries on transient errors.
If silent=True, raises RuntimeError instead of calling error_exit.
?
User-AgentheadersNtimeoututf-8HTTP :  for URL error: JSON parse error: Request failed after  attempts. Last error:            )urllibparse	urlencoderequestRequest
USER_AGENTrangeurlopenreaddecoderE   loadsrJ   	HTTPErrorrO   reasontimesleepRETRY_DELAYRuntimeErrorrP   URLErrorJSONDecodeError)urlparamsretriessilentreq
last_errorattemptresprawexcmsgs   &&&&       r9   http_getr      s    Ci&,,0088
..
 
 |Z.H
 
ICJGaK(	.''R'88Diik((1zz# 98 )( "'*A*
NC3sO+ 988 ||%% 	' 
"SZZLcUCJxx//

;011&z22:&&||$$ 	.&szzl3JJJ{W,--## 	.-cU3JJJ{W,--	.sg   6)D5D
DDDDI>AF;F;F;; II0HI+I,&IIc                   V'       d.   V R,           \         P                  P                  V4      ,           p \         P                  P	                  V R\
        /R7      pRp\        ^V^,           4       FW  p \         P                  P                  V^R7      ;_uu_ 4       pVP                  4       P                  R4      uuRRR4       u # 	  RV RV 2p	V'       d   \#        V	4      h\%        V	4       R#   + '       g   i     K  ; i  \         P                  P                   d   pRTP                   RTP                   R	T  2pTP                  R9   d)   \        P                  ! \         T,          4        Rp?EK  T'       d   \#        T4      h\%        T4        Rp?EK3  Rp?i\         P                  P&                   d=   pR
TP                   2p\        P                  ! \         T,          4        Rp?EK  Rp?ii ; i)zp
Like http_get but returns raw text instead of parsed JSON.
Useful for APIs that may return non-JSON responses.
rR   rS   rT   NrV   rX   rY   rZ   r[   r\   r^   r_   r`   )re   rf   rg   rh   ri   rj   rk   rl   rm   rn   rJ   rp   rO   rq   rr   rs   rt   ru   rP   rv   )
rx   ry   rz   r{   r|   r}   r~   r   r   r   s
   &&&&      r9   http_get_textr      sy   
 Ci&,,0088
..
 
 |Z.H
 
ICJGaK(	.''R'88Dyy{))'2 98 )  "'*A*
NC3sO# 988||%% 	' 
"SZZLcUCJxx//

;011&z22:&&||$$ 	.&szzl3JJJ{W,--	.sU   6)D	C5>
D	5D D	D		H(AF% F%F%% HH0G>>Hc                *   VP                  R4      p\        P                  P                  V VR\        RR/R7      pRp\        ^V^,           4       Fm  p \        P                  P                  V^R7      ;_uu_ 4       pVP                  4       P                  R4      p\        P                  ! V4      uuRRR4       u # 	  \%        RV RV 24       R#   + '       g   i     K  ; i  \        P                  P                   dm   p	RT	P                   R	T	P                   2pT	P                  R9   d)   \        P                   ! \"        T,          4        Rp	?	EK  \%        T4        Rp	?	EK  Rp	?	i\        P                  P&                   d=   p	R
T	P                   2p\        P                   ! \"        T,          4        Rp	?	EKw  Rp	?	i\        P(                   d3   p	RT	 2p\        P                   ! \"        T,          4        Rp	?	EK  Rp	?	ii ; i)zU
Perform an HTTP POST with a plain-text body (for Overpass QL).
Returns parsed JSON.
rX   rS   zContent-Typez!application/x-www-form-urlencoded)rG   rU   NrV   rY   rZ   r\   r]   zPOST failed after r_   r`   )encodere   rh   ri   rj   rk   rl   rm   rn   rE   ro   rJ   rp   rO   rq   rr   rs   rt   rP   rv   rw   )
rx   data_strrz   encodedr|   r}   r~   r   r   r   s
   &&&       r9   	http_postr      s   
 oog&G
..
 
 *?
 ! C JGaK(	.''R'88Diik((1zz# 98 )$ #G9,CJ<PQ! 988 ||%% 	' 
"SZZL9Jxx//

;011:&&||$$ 	.&szzl3JJJ{W,--## 	.-cU3JJJ{W,--	.sa   )C)>5C3
C)C& C)&C))HAE/E// HH0GHH &HHc                "   R\         P                  P                  V 4      ,           pRp\         F  p \	        W1^R7      u # 	  \        RT;'       g    R 24       R#   \
         d    RT R2p KC  \         d   pT RT 2p Rp?KZ  Rp?ii ; i)	a  POST an Overpass QL query, trying each URL in OVERPASS_URLS in turn.

A single public Overpass mirror can be rate-limited or down; trying the
next mirror before giving up turns a flaky outage into a retry. Returns
parsed JSON. Falls through to error_exit if every mirror fails.
zdata=N)rz   zmirror z exhausted retriesrZ   z)All Overpass mirrors failed. Last error: unknown)re   rf   quoteOVERPASS_URLSr   
SystemExit	ExceptionrP   )query	post_datar}   rx   r   s   &    r9   overpass_queryr     s     &,,,,U33IJ	SQ77  
3J4K4K)3LM  	"3%'9:J 	53%J	s#   AB2B;B<B		Bc                |   Rp\         P                  ! V 4      p\         P                  ! V4      p\         P                  ! W ,
          4      p\         P                  ! W1,
          4      p\         P                  ! V^,          4      ^,          \         P                  ! V4      \         P                  ! V4      ,          \         P                  ! V^,          4      ^,          ,          ,           p	^V,          \         P                  ! \         P
                  ! V	4      \         P
                  ! ^V	,
          4      4      ,          # )zAReturn distance in metres between two lat/lon points (Haversine).i6a )mathradianssincosatan2sqrt)
lat1lon1lat2lon2Rphi1phi2dphidlamas
   &&&&      r9   haversine_mr   -  s    A<<D<<D<<$D<<$D	$(	q	 88D>DHHTN*TXXdQh-?1-DD
EAq54::diilDIIa!e,<===r;   c                l    RV RRRVR^/p\         P                  ! \        4       \        \        VR7      # )z8Geocode a free-text query. Returns list of result dicts.qformatrE   limitaddressdetailsry   )rr   rs   NOMINATIM_RATE_LIMITr   NOMINATIM_SEARCH)r   r   ry   s   && r9   nominatim_searchr   =  s<     	%&%!	F 	JJ#$$V44r;   c                l    RV RVRRR^/p\         P                  ! \        4       \        \        VR7      # )z6Reverse geocode lat/lon. Returns a single result dict.latlonr   rE   r   r   )rr   rs   r   r   NOMINATIM_REVERSE)r   r   ry   s   && r9   nominatim_reverser   I  s<     	##&!	F 	JJ#$%f55r;   c                    \        V ^R7      pV'       g   \        RV  24       V^ ,          p\        VR,          4      \        VR,          4      VP                  RV 4      3# )zY
Geocode a query and return (lat, lon, display_name).
Exits with error if nothing found.
r   zCould not geocode: r   r   display_name)r   rP   floatget)r   resultsrs   &  r9   geocode_singler   U  sT    
 uA.G(01
A5?E!E(OQUU>5-IIIr;   c                   V'       d   TMW3.pRp	V'       d   RV R2p	. p
V FM  w  rV
P                  RV RV RV	 RV RV RV R24       V
P                  R	V RV RV	 RV RV RV R24       KO  	  R
P                  V
4      pRV RV R2# )a2  Build an Overpass QL query for nearby POIs around a point.

If ``tag_pairs`` is provided, the query unions across every
``(key, value)`` pair (used for categories like ``bakery`` that are
tagged under more than one OSM key). Otherwise falls back to the
single ``tag_key``/``tag_val`` pair for back-compat.
 ["religion"=""]  node[""="z(around:,);  way["
[out:json][timeout:25];
(

);
out center ;
appendjoin)tag_keytag_valr   r   radiusr   religion	tag_pairspairsreligion_filter
body_lineskvbodys   &&&&&&&&      r9   build_overpass_nearbyr   e  s     #I');(<EO)(26JqcQCr/!2vhauAcU".	
 	aSA3b 1vhauAcU".	
  99Z D& WC		!r;   c	                (   V'       d   TMW3.p	Rp
V'       d   RV R2p
. pV	 FS  w  rVP                  RV RV RV
 RV RV RV RV R24       VP                  R	V RV RV
 RV RV RV RV R24       KU  	  R
P                  V4      pRV RV R2# )zwBuild an Overpass QL query for POIs within a bounding box.

See ``build_overpass_nearby`` for ``tag_pairs`` semantics.
r   r   r   r   r   (r   r   r   r   r   r   r   r   )r   r   southwestnortheastr   r   r   r   r   r   r   r   r   s   &&&&&&&&&      r9   build_overpass_bboxr     s     #I');(<EO)(26JqcQCr/!2wavQugQtfB0	
 	aSA3b 1wavQugQtfB0	
  99Z D& WC		!r;   c                4   . pV  EF  pVR,          R8X  d6   VP                  R/ 4      pVP                  R4      pVP                  R4      pM"VP                  R4      pVP                  R4      pVe   Vf   Ks  VP                  R/ 4      pVP                  R4      ;'       g    VP                  R4      ;'       g    R	p	. p
R F/  pVP                  V4      pV'       g   K  V
P                  V4       K1  	  V
'       d   R
P                  V
4      MR	pRT	RTRTRTRVP                  RR	4      RVP                  RR	4      RRV RV 2RVP                  4        UUu/ uF  w  rVR9  g   K  WbK  	  upp/pR F&  w  ppVP                  V4      pV'       g   K!  VVV&   K(  	  Ve1   Ve-   \	        WWg4      p\        V^4      VR&   RV RV RV RV 2VR&   VP                  V4       EK  	  V'       d"   RV^ ,          9   d   VP                  R R7       V# u uppi )z}
Parse Overpass elements into a clean list of POI dicts.
If ref_lat/ref_lon are provided, computes distance and sorts by it.
typewaycenterr   r   tagsnamename:enr   , addressosm_typeosm_ididmaps_urlz0https://www.google.com/maps/search/?api=1&query=r   
distance_mz.https://www.google.com/maps/dir/?api=1&origin=z&destination=directions_urlc                     V R ,          # )r    ps   &r9   <lambda>)parse_overpass_elements.<locals>.<lambda>  s    !L/r;   key)addr:housenumberaddr:street	addr:city>   r   r   r   r   r   ))cuisiner   )opening_hourshours)phoner   )websiter   )r   r   r   itemsr   roundsort)elementsref_latref_lonplaceselr   el_latel_lonr   r   
addr_partspart_keyvaladdress_strr   r   placesrc_keydst_keydist_ms   &&&                 r9   parse_overpass_elementsr    s<   
 Ff:VVHb)FZZ&FZZ&FVVE]FVVE]F>V^vvfb!xx<<488I#6<<" 
HH((8$Cs!!#& I 0:dii
+r vr*tR( J6(RSTZS[\!%!- M M 
&!
GW ((7#Cs!$g!
 7#6 6BF"'"2E,")1WIxq2 "# 	e D ,&)+12MGs   H&(H&c                   RP                  V P                  4      p\        V^R7      pV'       g   \        RVR. R^ R\        /4       R# . pV EFs  pVP                  R. 4      pTP                  R	VP                  R	4      ;'       g    VP                  R
R4      R
VP                  R
R4      R\        VR,          4      R\        VR,          4      RVP                  RR4      RVP                  RR4      RVP                  RR4      RVP                  RR4      RR\        V4      ^ 8  d   \        V^ ,          4      MRR\        V4      ^8  d   \        V^,          4      MRR\        V4      ^8  d   \        V^,          4      MRR\        V4      ^8  d   \        V^,          4      MR/RVP                  R4      /
4       EKv  	  \        RVRVR\        V4      R\        /4       R# )z,Geocode a place name and return top results. r   r   r   countdata_sourceNboundingboxr   r   r   r   r   r   r7   r   r   bounding_boxmin_latmax_latmin_lonmax_lon
importance)	r   r   r   rH   DATA_SOURCEr   r   r   len)argsr   r   r   itembbs   &     r9   
cmd_searchr!    s   HHTZZ EU!,C521;	
 	 	GXXmR(DHHV,LL0LDHH^R8E$u+.E$u+.DHHVR0DHHZ4DHHZ4DHHXr23r7Q;5A<D3r7Q;5A<D3r7Q;5A<D3r7Q;5A<D	 DHH\2
 	 ( uws7|{	 r;   c                     \        V P                  4      p\        V P                  4      pRXu;8:  d   ^Z8:  g   M \	        R4       RXu;8:  d   ^8:  g   M \	        R4       \        W4      pRV9   d   \	        RVR,           24       VP                  R/ 4      p\        RTRTR	VP                  R	R
4      RRVP                  RR
4      RVP                  RR
4      RVP                  RR
4      RVP                  RR
4      RVP                  R4      ;'       g,    VP                  R4      ;'       g    VP                  RR
4      RVP                  RR
4      RVP                  RR
4      RVP                  RR
4      RVP                  RR
4      RVP                  RR
4      /
RVP                  RR
4      RVP                  RR
4      R\        /4       R#   \         d    \	        R4        ELi ; i)z8Reverse geocode coordinates to a human-readable address.#LAT and LON must be numeric values.$Latitude must be between -90 and 90.'Longitude must be between -180 and 180.rJ   zReverse geocode failed: r   r   r   r   r   house_numberroadneighbourhoodsuburbcitytownvillagecountystatepostcodecountrycountry_coder   r   r  NL)	r   r   r   
ValueErrorrP   r   r   rH   r  )r  r   r   rG   r   s   &    r9   cmd_reverser5  %  s   :DHHoDHHo 3"9:C3<=S&D$-d7m_=>hhy"%G4W[[<W[[4W[["=W[[26gkk&1 < <!(V!4< <!(Y!;W[[26W[["5W[[R8W[[B7W[[<
 	txx
B/txx"-{)   :89:s   *G G,+G,c                p   \        V RR4      '       d   \        V P                  \        4      '       d*   RP	                  V P                  4      P                  4       M"\        V P                  4      P                  4       pV'       g   \        R4       \        V4      w  r#pM+ \        V P                  4      p\        V P                  4      p. p\        V RR4      '       d   VP                  V P                  4       \        V RR4      '       d   VP!                  V P"                  4       \        \$        P'                  R V 4       4      4      pV'       g   \        R	4       V Uu. uF  qf\(        9  g   K  VNK  	  ppV'       dP   \        R
\+        V4      ^8  d   RMR RRP	                  R V 4       4       RRP	                  \,        4       24       \/        V P0                  4      p\/        V P2                  4      p	V^ 8:  d   \        R4       V	^ 8:  d   \        R4       / p
V F  p\5        V4      p\6        P9                  V4      p\;        RRXXWWR7      p\=        V4      pVP9                  R. 4      p\?        VW#R7       F:  pVVR&   VP9                  RR4      VP9                  RR4      3pVV
9  g   K5  VV
V&   K<  	  K  	  \A        V
PC                  4       R R7      RV	 p\E        RXRXRVRVR\+        V4      RVR \F        /4       R#   \        \        3 d    \        R4        ELbi ; iu upi )!u#  Find nearby POIs using the Overpass API.

Accepts either explicit coordinates (``lat``/``lon``) or a free-form
address via ``--near`` (auto-geocoded through Nominatim). Supports
multiple categories in one call — results are merged, deduplicated
by ``osm_type+osm_id``, sorted by distance.
nearNr  z1--near must be a non-empty address or place name.z7Provide numeric LAT and LON, or use --near "<address>".category_listr7   c              3   T   "   T F  q'       g   K  VP                  4       x  K   	  R # 5iN)lower.0cs   & r9   	<genexpr>cmd_nearby.<locals>.<genexpr>u  s     #Gz!QIAGGIIzs   	((z9Provide at least one category (positional or --category).zUnknown categoriesyr   c              3   8   "   T F  p\        V4      x  K  	  R # 5ir:  )reprr<  s   & r9   r?  r@  |  s     2'Qa's   z. Valid categories: z+Radius must be a positive integer (metres).!Limit must be a positive integer.r   r   r  r  r  r   r   r   c                 8    V P                  R \        R4      4      # )r   inf)r   r   r   s   &r9   r   cmd_nearby.<locals>.<lambda>  s    aeeL%,7r;   r   
center_lat
center_lon
categoriesradius_mr  r   r  )$getattrr5   r7  r6   r   stripstrrP   r   r   r   r   	TypeErrorr4  extendr8  r   r7   dictfromkeysr4   r  VALID_CATEGORIESintr   r   r:   RELIGION_FILTERr   r   r   r  sortedvaluesrH   r  )r  
near_queryr   r   _rM  r>  r   r   r   mergedr7   r   r   r   r   r  r  r   r  s   &                   r9   
cmd_nearbyr^  U  s    tVT""4>tyy$4O4OSXXdii(..0UXY]YbYbUcUiUiUk
JK$Z0!	T/C/C Jt_d++$,,-tZ&&$--(dmm#Gz#GGHJNO$?*Q(>qq*G?s7|a'7eSAyy2'223 4!!%+;!< =?	
 F_E{@Az67
 Fh'	"&&x0%dD#sF/7NU#77:r*,XsPE (E*99Z,eii".EFC & #s Q   7 uF
 sszvs6{v{ o :& 	TRS	T @s   #*L 'L38L3L0/L0c                   RP                  V P                  4      pRP                  V P                  4      pV P                  P	                  4       pV\
        9  d%   \        RV RRP                  \
        4       24       \        V4      w  rEp\        V4      w  rxp	\
        V,          p
\         RV
 RV RV RV RV R2p\        V4      pVP                  R	4      R
8w  d/   \        RVP                  RVP                  R	R4      4       24       VP                  R. 4      pV'       g   \        R4       V^ ,          pVP                  R^ 4      pVP                  R^ 4      p\        VR,          ^4      p\        V^<,          ^4      p\        WEWx4      p\        RRVRVRVRV/RRVRV	RVRV/RVRVR\        V^4      RVR\        V^4      R\        VR,          ^4      R\        /	4       R # )!z;Calculate road distance and travel time between two places.r  Invalid mode ''. Choose from: r   /r   ;z?overview=false&steps=falserO   OkOSRM routing failed: rN   unknown errorroutes)No route found between the two locations.distanceduration  originr   r   r   r   destinationmodedistance_kmr   duration_minutesduration_secondsstraight_line_kmr  N)r   rl  torn  r;  OSRM_PROFILESrP   r   	OSRM_BASEr   r   r   r   rH   r  )r  origin_querydestination_queryrn  o_lato_lono_named_latd_lond_nameprofilerx   	osrm_datarg  router   
duration_sro  duration_min
straight_ms   &                   r9   cmd_distancer    s   -L)		)D= ^D6)9$))M:R9STU *,7E&)*;<E&D!G+Qwiq'5'5'5'
%	'  I}}V$#}}Y	fo(NOPR	

 ]]8R(F>?!9E99Z+J99Z+Jd*A.Kb!,L U58JLFEE	
 	-FEE	
 	DKE*a0LE*a0E*t"3Q7K' r;   c                    V ^<8  d   \        V 4       R2# V ^<,          pV^<8  d   \        V^4       R2# \        V^<,          4      p\        V^<,          4      pV RV R2# )z,Format seconds into a human-readable string.sz minzh min)r   rW  )secondsminutesr   	remainings   &   r9   _format_durationr    so    |.!##lG|#$D))2Egl#IWByk%%r;   c                X    V R8  d   \        V 4       R2# \        V R,          ^4       R2# )z+Format metres into a human-readable string.rk  z mz km)r   )metress   &r9   _format_distancer    s3    }-##FTM1%&c**r;   c                   RP                  V P                  4      pRP                  V P                  4      pV P                  P	                  4       pV\
        9  d%   \        RV RRP                  \
        4       24       \        V4      w  rEp\        V4      w  rxp	\
        V,          p
\         RV
 RV RV RV RV R2p\        V4      pVP                  R	4      R
8w  d/   \        RVP                  RVP                  R	R4      4       24       VP                  R. 4      pV'       g   \        R4       V^ ,          pVP                  R^ 4      pVP                  R^ 4      p. p^ pVP                  R. 4       EF  pVP                  R. 4       EF  pVP                  R/ 4      pVP                  R^ 4      pVP                  R^ 4      pVP                  RR4      pVP                  RR4      pVP                  RR4      pVR8X  d   V'       d   RV 2MRpEM)VR8X  d   RpEMVR8X  d   V'       d	   RV R V 2MRV 2pEM VR!8X  d   V'       d   R"V 2MR#pMVR$8X  d   V'       d	   R%V R V 2MR%V 2pMVR&8X  d   V'       d	   R'V R(V 2MR'V R)2pMVR*8X  d   V'       d   R+V 2MR,pMVR-8X  d   V'       d   R.V 2MR/pMVR08X  d   V'       d	   R1V R V 2MR1V 2pMcVR28X  d   V'       d	   R3V R4V 2MR3V 2pMFVR58X  d   V'       d   R6V 2MR7pM/VR88X  d   V'       d   R9V 2MR:pMV RV RV 2P                  4       pV^,          pVP                  R;VR<VR\        V4      R=\        V^4      R\        V4      R>\        V^4      R?VRV/4       EK  	  EK  	  \!        R@RAVRBVRCVRDV/RERAVRBV	RCVRDV/RFVRG\        V4      RH\        V^4      RI\        V4      RJ\        V^4      RVRK\#        V4      RL\$        /
4       RM# )Nz8Get turn-by-turn directions between two places via OSRM.r  r`  ra  r   rb  r   rc  z?overview=false&steps=truerO   rd  re  rN   rf  rg  rh  ri  rj  legsstepsmaneuverr   r   modifierr   departz
Depart on DepartarrivezArrive at destinationturnzTurn z onto znew namezContinue onto ContinuemergezMerge forkz	Take the z fork onto z fork
roundaboutzEnter roundabout, exit onto zEnter roundaboutrotaryzEnter rotary, exit onto zEnter rotaryzend of roadzAt end of road, turn continuez	Continue z on zon rampzTake ramp onto z	Take rampzoff rampzTake exit onto z	Take exitstepinstructionr   r  	road_namerl  r   r   r   r   rm  rn  total_distancetotal_distance_mtotal_durationtotal_duration_s
step_countr  N)r   rl  rs  rn  r;  rt  rP   r   ru  r   r   rP  r   r  r   r  rH   r  r  )r  rv  rw  rn  rx  ry  rz  r{  r|  r}  r~  rx   r  rg  r  r   r  r  step_numlegr  r  	step_diststep_dur	step_namer  m_typer  s   &                           r9   cmd_directionsr    sk   -L)		)D= ^D6)9$))M:R9STU *,7E&)*;<E&D!G+Qwiq'5'5'5'
$	&  I}}V$#}}Y	fo(NOPR	

 ]]8R(F>?!9E99Z+J99Z+J EHyy$GGGR(Dxx
B/HQ/IQ/H,I Z4H VR0F !:C
9+68#56!ENhZvi[AV[\d[eTf:%>Gyk:Z7"FOxjykBW]^f]gUh6!NW	(;ykJ_hiqhrrw]x<'LU <YKH[m8#HQ 8DWe=(U^ 5hZvi[Qf{  }E  |F  eG:%GP	(4	{CXabjakVl9$?H	{;k:%?H	{;k!'(1YK@FFHMHLLx{/	:uY2/9uXq1yv	 	K ) %b LFEE	
 	-FEE	
 	d.z:eJ2.z:eJ2ec%jk) r;   c                &    \        V P                  4      p\        V P                  4      pRXu;8:  d   ^Z8:  g   M \	        R4       R Xu;8:  d   ^8:  g   M \	        R4       RpRpRpRp RVRV/p\        \        VRR7      p\        V\        4      '       Ed`   VP                  R	4      pVP                  R
4      pVP                  R/ 4      p	\        V	\        4      '       dp   V	P                  R^ 4      p
\        V	P                  R^ 4      4      pV	P                  R^ 4      pV
^ 8  d   RMRpV \        V
4      R RVR 2pV'       d	   V RVR 2pMVP                  R4      '       d   VR,          p\        V\        4      '       do   VP                  R^ 4      p
\        VP                  R^ 4      4      pVP                  R^ 4      pV
^ 8  d   RMRpV \        V
4      R RVR 2pV'       d	   V RVR 2pRpT'       g9   \        T^,          4      pT^ 8  d	   RTR R2pMR\        T4      R R2pRT 2pRp\        RTRTRTRTRTRTR\         /4       R#   \         d    \	        R4        ELGi ; i  \        \        \        3 d     Li ; i)!z
Get timezone information for a lat/lon coordinate.

Strategy:
  1. Try TimeAPI.io (free, no key, supports coordinate-based lookup).
  2. Fallback: derive UTC offset approximation from longitude.
r#  r$  r%  Nlatitude	longitudeT)ry   r{   timeZonecurrentLocalTimecurrentUtcOffsetr   r  r  +-02d:standardUtcOffsetz
timeapi.ioz:00UTCz&longitude approximation (longitude/15)r   r   timezone
utc_offsetcurrent_timesourcer  r2  r3  )r   r   r   r4  rP   r   TIMEAPI_BASEr5   rT  r   absru   KeyErrorrR  r   rH   r  )r  r   r   timezone_strtimezone_srcr  r  ry   tz_dataoffset_infoohomos_signoffset_info2approx_offset_hs   &               r9   cmd_timezoner  z  s   :DHHoDHHo 3"9:C3<=LLLJc;4<tDgt$$";;z2L";;'9:L!++&8"=K+t,, __Wa0A67!ooi3 Ags3 $vc"gc]!Bs8<
$.<qS	!:J011&':;lD11%))'15B\--i;<B&**9a8C"$'3sD$(6#b'#a3x!@J(2|1SI%>
'L
 b/a_S15JS1#6c:JZL)?
 m  :89:R (I. s+   *I -C>I7 ,BI7 I43I47JJc                    \        V P                  4      p\        V P                  4      p\        V P                  4      p\        V P                  4      p\        XX4      p\        W4      p\        XX4      p\        W$4      pV P                  P                  4       p	V	\        9  d%   \        RV	 RRP                  \        4       24       \        V P                  4      p
V
^ 8:  d   \        R4       \!        V	4      p\"        P%                  V	4      p\'        RRWWWhWVR7	      p\)        V4      pVP%                  R. 4      pWV,           ^,          pWx,           ^,          p\+        VVVR	7      pV F  pV	VR
&   K
  	  \-        RRVRVRVRV/R
V	R\/        V4      RVR\0        /4       R#   \
         d    \        R4        ELki ; i)z7Find POIs within a bounding box using the Overpass API.z0All coordinate arguments must be numeric values.zUnknown category 'z'. Valid categories: r   rE  NrF  r  rG  r7   r  r   r   r   r   r  r   r  )r   r   r   r   r   r4  rP   r  maxr7   r;  r4   r   rV  rW  r   r:   rX  r   r   r   r  rH   r  r  )r  r   r   r   r   r   r   r   r   r7   r   r   r   r   r   r  rK  rL  r  r   s   &                   r9   cmd_bboxr    s   GTYYTYYTYYTYY
 dOEOEdODOD}}""$H}$ 
 +!!%+;!< =?	

 

OEz67(#I""8,HdE %IOE 
Cwwz2&H -1$J+"J$Xz-79F  *  UTUT	
 	xs6{v{ M  GEFGs   AF( (GGc                   RP                  V P                  4      p\        V^R7      pV'       g   \        RV 24       V^ ,          pVP	                  R. 4      p\        V4      ^8  d   \        RV 24       \        V^ ,          4      p\        V^,          4      p\        V^,          4      p\        V^,          4      pWV,           ^,          p	\        WWWg4      R,          p
\        WW4      R,          p\        W,          ^4      p\        RVRVP	                  RR	4      R
\        VR
,          4      R\        VR,          4      RVP	                  RR	4      RVP	                  RR	4      RRVRVRVRV/RR\        V^4      R\        V
^4      /RVRVP	                  RR	4      RVP	                  RR	4      R\        /4       R# )1Get bounding box and area info for a named place.r  r   zCould not find place: r  z$No bounding box data available for: rk  r   r   r   r   r   r   r7   r  r   r   r   r   
dimensionswidth_km	height_kmapprox_area_km2r   r   r  N)r   r  r   rP   r   r  r   r   r   rH   r  )r  r   r   r  r   r  r  r  r  avg_latr  r  r  s   &            r9   cmd_arear  	  s   HHTZZ E
5
*C+E734q6D	-	$B
2w{9%ABBqElGBqElGBqElGBqElG  A%GGg?$FIGg?$FHI0!4O4d5k*d5k*,R0WWWW	
 	x+y!,
 	?488J3488Hb1;) r;   c            	         \         P                  ! R R\         P                  RR7      p V P                  RRRR7      pVP	                  RR	R
R7      pVP                  RRRR7       VP	                  RRRR7      pVP                  RRR7       VP                  RRR7       VP	                  RRRRP                  \        4       2\         P                  R7      pVP                  RRRRR 7       VP                  RRRR!R 7       VP                  R"RRR#R 7       VP                  R$RR%R&R'7       VP                  R(R)R*. R+R,R-7       VP                  R.R/R0\        R1R2R37       VP                  R4R5^
\        R6R7R37       VP	                  R8R9R:\         P                  R7      pVP                  R;RR<R7       VP                  R=RRR>R?R@7       VP                  RARBRC\        \        P                  4       4      RDRE7       VP	                  RFRGRH\         P                  R7      pVP                  R;RR<R7       VP                  R=RRR>R?R@7       VP                  RARBRC\        \        P                  4       4      RDRE7       VP	                  RIRJRKR7      pVP                  RRR7       VP                  RRR7       VP	                  RLRMRNRP                  \        4       2\         P                  R7      pVP                  RORPR7       VP                  RQRRR7       VP                  RSRTR7       VP                  RURVR7       VP                  R"RWR7       VP                  R4R5^\        R6RXR37       VP	                  RYRZR[R7      p	V	P                  R\RR]R7       V # )^zmaps_client.pyzCLI maps tool: geocoding, reverse geocoding, POI search, routing, directions, timezone, and area lookup. Powered by OpenStreetMap, OSRM, Overpass, and TimeAPI.io. No API keys required.a  Examples:
  maps_client.py search Times Square
  maps_client.py reverse 40.758 -73.985
  maps_client.py nearby 40.758 -73.985 restaurant --radius 800
  maps_client.py distance New York --to Los Angeles --mode driving
  maps_client.py directions Paris --to Berlin --mode driving
  maps_client.py timezone 48.8566 2.3522
  maps_client.py bbox 40.70 -74.02 40.78 -73.95 restaurant
  maps_client.py area Manhattan)progdescriptionformatter_classepilogcommandTCOMMAND)destrequiredmetavarsearchz$Geocode a place name to coordinates.z>Search for a place by name and return coordinates and details.)helpr  r   r  z Place name or address to search.)nargsr  reversez*Reverse geocode coordinates to an address.zCConvert latitude/longitude coordinates to a human-readable address.r   zLatitude (decimal degrees).)r  r   zLongitude (decimal degrees).nearbyz'Find nearby places of a given category.a  Find points of interest near a location using the Overpass API.
Provide either LAT/LON, or use --near "<address>" to auto-geocode.
Categories can be specified positionally OR repeated via --category
to merge multiple types in one query (e.g. --category bar --category cafe).
Categories: r   )r  r  r  rR   Nz8Center latitude (decimal degrees). Omit if using --near.)r  defaultr  z9Center longitude (decimal degrees). Omit if using --near.r7   zHPOI category (use --help for full list). Omit if using --category flags.z--nearPLACEzEAddress, city, or landmark to search around (geocoded via Nominatim).)r  r  r  z
--categoryr   r8  CATu8   POI category (repeatable — adds a type to the search).)actionr  r  r  r  z--radiusz-ri  METRESz'Search radius in metres (default: 500).)r  r   r  r  z--limitz-nNz(Maximum number of results (default: 10).ri  z(Calculate road distance and travel time.zCalculate road distance and estimated travel time between two places.
Example: maps_client.py distance New York --to Los Angelesrl  zOrigin address or place name.z--toDESTz-Destination address or place name (required).)r  r  r  r  z--modez-mr<   zTravel mode (default: driving).)r  choicesr  
directionsz/Get turn-by-turn directions between two places.z~Get step-by-step navigation directions between two places.
Example: maps_client.py directions Paris --to Berlin --mode drivingr  z)Get timezone information for coordinates.zALook up timezone and current local time for a lat/lon coordinate.bboxz Find POIs within a bounding box.zSearch for points of interest within a geographic bounding box.
Tip: use the 'area' command to find bounding boxes for named places.
Categories: r   zFirst corner latitude.r   zFirst corner longitude.r   zSecond corner latitude.r   zSecond corner longitude.zPOI category to search for.z(Maximum number of results (default: 20).arear  z}Look up a place by name and return its bounding box, dimensions, and approximate area. Useful as input to the 'bbox' command.r  z@Place name to look up (e.g., 'Manhattan' or 'downtown Seattle').)argparseArgumentParserRawDescriptionHelpFormatteradd_subparsers
add_parseradd_argumentr   rV  rW  r6   rt  keys)
parsersubp_search	p_reversep_nearbyp_distp_dirp_tzp_bboxp_areas
             r9   build_parserr  ?  s   $$$
 !<<.F* 

Y)2   4C ~~3T  H
 s/   9Y  I
 5'DE5'EF ~~6  99%567	9 !<<  H S$G   S$H   #tW   WT   XORG  
 D#x6  
 4c7   ^^7I !<<  F ,   cD&<   $]'')*.	   NN>R !<<  E 
,   
cD&<   
$]'')*.	   >>8W  D
 	e"?@e"@A ^^/99%5679 !<<  	F %=>
%>?
%>?
%?@

)FG
4c7   ^^@K	  F sO  
 Mr;   c                     \        4       p V P                  4       pR \        R\        R\        R\
        R\        R\        R\        R\        /pVP                  VP                  4      pVf   \        R	VP                   24       V! V4       R# )
r  r  r  ri  r  r  r  r  NzUnknown command: )r  
parse_argsr!  r5  r^  r  r  r  r  r  r   r  rP   )r  r  dispatchhandlers       r9   mainr    s|    ^F D 	jkjlnlhh	H ll4<<(G&t||n56DMr;   __main__)amenityr   )r  r   )r  r   )shopr   )r  r   )r	  convenience)r  r   )r  r   )r  r	   )r  doctors)r  r   )tourismr   )r  r   )r  r   )r	  r   )r	  books)r	  r   )r  r   )r  r   )r  fuel)r  r   )aeroway	aerodrome)railwaystation)highwayr   )r  r   )r  r   )r  r   )r  r   )r  r   )r  r   )r  r   )r  r    )r  r!   )r  r"   )r  r#   )r  r$   )r  r%   )r  r&   )r  r'   )r  place_of_worship)leisurer+   )r  fitness_centre)r  r-   )r  r.   )r  r/   )   )   )NN)7__doc__r  rE   r   osrL   rr   urllib.errorre   urllib.parseurllib.requestrj   r  r   r   r   OVERPASS_APIru  r  r   MAX_RETRIESrt   r4   rX  rY  r  rV  r:   rt  rH   rP   r   r   r   r   r   r   r   r   r   r   r  r!  r5  r^  r  r  r  r  r  r  r  r  r  __name__r   r;   r9   <module>r!     s       	 
     :
'@ A  .3
 "!$>	@   <2< ,< 
+	< ,.CD< 3< 0< 0< /< /< 2<  -!<" 3#<$ 1%<( 3)<* -+<, /-<0 
+1<2 ,3<6 ,7<8 /9<: 1;<< /=<> 0?<@ ,A<B 0C<D 2E<F 6G<J .K<L .M<N /O<P 1Q<R 
+S<V .W<X 2Y<Z /.43888
,	652/w<@  -,,./ 
 yvv:
 {5 #L #K B &1 "RJ8	> 	5	6	J B<Kd(^)`St=H	&+mhI`8~/lyx, zF r;   