
    /6i@                        S r SSKrSSKrSSKrSSKJr  SSKrSSKJ	r
  SSKrSSKrSSKJr  SSKJrJrJr  SSKJr  SSKJr  SrS	rS
 rSS jrS\4S jrS rS rS rS r S\4S jr!S r"S r#S r$S r%\&S:X  a  \%" 5         gg)a  
Extract smooth hotspot contours from heatmap GeoTIFFs using continuous raster analysis.

Instead of merging rectilinear grid cells (which creates blocky boundaries),
this script treats the raster as continuous and uses skimage.measure.find_contours
to extract smooth contour polygons at a threshold that captures the target area.

Outputs:
 - PNG overlay showing contours on the heatmap
 - GeoJSON with smooth polygon boundaries (not blocky grid cells)
    N)Path)shapes)shapeMultiPolygonPolygon)unary_union)measureHOTSPOT_TARGET_AREA_KM2HOTSPOT_TARGET_PERCENTILEc                 x   U b  Ub  [        S5      eU b"   [        U 5      nUS::  a  [        S5      eSUS.$ Ub+   [        U5      nSUs=:  a  S	:  d  O  [        S
5      eSUS.$ [        R                  " [        5      n[        R                  " [
        5      nUb  Ub  [        S[         S[
         S35      eUb"   [        U5      nUS::  a  [        S5      eSUS.$ Ub+   [        U5      nSUs=:  a  S	:  d  O  [        S
5      eSUS.$ [        S[         S[
         S35      e! [          a  n[        SU  S35      UeSnAff = f! [          a  n[        SU S35      UeSnAff = f! [          a  n[        S[         SU S35      UeSnAff = f! [          a  n[        S[
         SU S35      UeSnAff = f)z;Return the requested hotspot criteria (area or percentile).Nz:Cannot specify both --target-area and --target-percentile.r   z&Target area must be greater than zero.area)typevaluezInvalid target area ''.d   z%Percentile must be between 0 and 100.
percentilezInvalid percentile 'zCannot set both z and .zInvalid area in z: 'zInvalid percentile in zITarget criteria must be provided via --target-area, --target-percentile, z, or )
ValueErrorfloatosgetenvTARGET_AREA_ENV_VARTARGET_PERCENTILE_ENV_VAR)area_argpercentile_argr   excr   area_envpercentile_envs          2/data2/heatmap/scripts/extract_hotspot_contours.pyresolve_target_criteriar        s.     :UVV	L?Dqy !IJJ"T22 !	Q~.Jz'C' !HII(:>>
 yy,-HYY89N :+,?+@F_E``abcc	_?Dqy !IJJ"T22 !	q~.Jz'C' !HII(:>>
 
S
u%>$?q	B M  	L4XJbABK	L  	Q3N3C2FGSP	Q   	_/0C/DCzQSTU[^^	_  	q56O5PPSTbSccefgmpp	qs^    D& )E = E* ")F &
E0E  E
E'E""E'*
F4FF
F9F44F9c                 N   S/nU(       a  UR                  U5        / nU H#  nUR                  U R                  U5      5        M%     / n[        5       n[        U5       HC  nXv;  d  M
  UR	                  5       (       d  M!  UR                  U5        UR                  U5        ME     U$ )z6Find GeoTIFF heatmaps in the given location directory.z*road_density_*km.tif)extendglobsetsortedis_fileappendadd)location_dirextra_patternspatternsfilespatternunique_filesseen	file_paths           r   find_heatmap_rastersr1   X   s    '(H'E\&&w/0 L5DE]	 Y%6%6%8%8	*HHY #     raster_pathc                 8   U R                   R                  S5      nU(       d  [        SU R                   35      eUS   nUR	                  S5      (       d  [        SU S35      e [        USS 5      nX#S-  4$ ! [         a  n[        S	U S
35      UeSnAff = f)zPExtract the grid size label (e.g., '2km') and corresponding cell size in meters._z"Cannot parse grid size label from kmzGrid label 'z' does not end with 'km'.NzInvalid grid label 'r   g     @@)stemsplitr   nameendswithr   )r3   parts
size_labelcell_kmr   s        r   parse_size_labelr@   i   s    ""3'E=k>N>N=OPQQrJt$$<
|3LMNNI
3B( '''  I/
|2>?SHIs   &A: :
BBBc                 h   [        US   5      n[        US   5      nX4-  nUS-  nX&-  nU [        R                  " U 5         n[        U5      S:X  a  [	        S5      e[        R
                  " U5      SSS2   n	[        [        U5      [        U	5      S-
  5      n
X   n[        SUS	 S
US	 S35        U$ )zHCalculate density threshold that captures approximately the target area.r      @B zNo valid data in rasterNr6            📊 Threshold: .1fz (captures ~u    km²))	absnpisfinitelenr   sortminintprint)data	transformtarget_area_km2pixel_widthpixel_heightpixel_area_m2pixel_area_km2target_pixels
valid_datasorted_valuesthreshold_idx	thresholds               r   #calculate_threshold_for_target_arear[   x   s     il#Ky|$L.M"Y.N $4M bkk$'(J
:!233 GGJ'"-M M*C,>,BCM,I		#l?3:Ov
VWr2   c                 j   U [         R                  " U 5      U S:  -     n[        U5      S:X  a  [        S5      e[         R                  " X!5      n[         R
                  " [         R                  " U 5      U S:  -  X:  -  5      n[        U5      nXE-  S-  n[        SUS SUS SUS S	US S
3	5        U$ )zPCalculate density threshold for the top N% of values, excluding zeros and nulls.r   z No valid non-zero data in rasterr   rE   rF   z (captures z% = ,/z non-zero pixels))rH   rI   rJ   r   r   sumrN   )rO   r   rW   rZ   pixel_counttotal_nonzeroactual_percents          r   "calculate_threshold_for_percentilerc      s     bkk$'4!845J
:!;<<
 j5I &&"++d+tax8T=NOPK
OM!1S8N		#k.9MTR]^_Q``abopqar  sD  E  Fr2   c                     US   S:X  a  [        XUS   5      $ US   S:X  a  [        XS   5      $ [        SUS    35      e)z9Calculate threshold based on area or percentile criteria.r   r   r   r   zUnknown criteria type: )r[   rc   r   )rO   rP   criterias      r   calculate_thresholdrf      sW    6!24HWDUVV	&	\	)1$8IJJ28F3C2DEFFr2   c                    [         R                  " [         R                  " U 5      U S:  -  X:  -  SS5      R                  [         R                  5      n[
        R                  " USS9nU(       d  [        R                  " / SQ/ US9$ / nU H  n/ nU H1  u  pUS   XS   -  -   nUS   XS	   -  -   nUR                  X45        M3     [        U5      S
:  d  MM   US   US   :w  a  UR                  US   5        [        U5      nUR                  (       a8  UR                  (       d%  UR                  S:  a  UR                  U5        M  M  M  M     U(       d  [        R                  " / SQ/ US9$ [!        U5      n/ n[#        U[        5      (       a  U/nO;[#        U[$        5      (       a  ['        UR(                  5      nOUb  [*        nO[*        n/ n[-        USS9 H:  u  nnUR                  S-  nUR.                  S-  nUR                  UUUUS.5        M<     [        R                  " USUS9$ ! [         a     GM  f = f)zHExtract smooth contours from raster using skimage.measure.find_contours.r   rD   g      ?)level)
cluster_idarea_km2perimeter_km)columnsgeometrycrs      rB      r6   )startrC   i  )ri   rj   rk   rm   rm   )rm   rn   )rH   whererI   astypeuint8r	   find_contoursgpdGeoDataFramer'   rJ   r   is_validis_emptyr   	Exceptionr   
isinstancer   listgeomsfinal_polygons	enumeratelength)rO   rP   rZ   rn   binary_maskcontourspolygonscontourcoordsrowcolxypoly	dissolveddissolved_partsclustersidxgeomrj   rk   s                        r   extract_contours_from_rasterr      s%    ((2;;t,q9T=NOQRTUV]]^`^f^fgK $$[<H(R]_ehii H HC!sq\11A!sq\11AMM1&!   v;!	!9r
*MM&),v==499q=OOD) <I=# , (R]_ehii H%IO)W%%$+	I|	,	,y/		(( Ha8	T99y({{U* (	
 	 9 HzsCC?  s   A.H33
IIpathc                    [         R                  " U 5       nUR                  S5      R                  S5      nUR                  nUb)  [
        R                  " X#:H  [
        R                  U5      nO:[
        R                  " [
        R                  " U5      U[
        R                  5      nUR                  nUR                  nUR                  nS S S 5        WWWW4$ ! , (       d  f       N= f)NrD   float32)rasterioopenreadrt   nodatarH   rs   nanrI   boundsrn   rP   )r   srcrO   r   r   rn   rP   s          r   load_rasterr      s    	t	xx{!!),88DNBFFD9D88BKK-tRVV<DggMM	 
 i'' 
	s   B7C
C*c                 0   [         R                  " SS9u  pgUR                  UR                  UR                  UR
                  4nUR                  USUSSS9  UR                  (       d7  UR                  U:w  a  UR                  U5      n	OUn	U	R                  USSS	S
S9  US   S:X  a  SUS   S S3n
O
SUS   S S3n
UR                  U R                   SU
 S3SSS9  UR                  UR                  UR                  5        UR                  UR                  UR
                  5        UR                  S5        [         R                   " 5         U R#                  U R                   S35      nUR%                  USSS9  [         R&                  " U5        U$ )z4Plot smooth contour hotspots overlay on the heatmap.)
      )figsizehotupperauto)cmapextentoriginaspectcyanwhiteg333333?ro   )ax	facecolor	edgecoloralpha	linewidthr   r   zTop r   g    km²u   ≥.0fzth percentileu    — z (smooth contours)   r   )fontsizepadoffz_hotspots.pngi,  tight)dpibbox_inches)pltsubplotsleftrightbottomtopimshowemptyrn   to_crsplot	set_titler9   set_xlimset_ylimaxistight_layout	with_namesavefigclose)r3   rO   r   
raster_crshotspots_gdfre   figr   r   hotspots_plottitle_suffixout_paths               r   plot_hotspotsr     s   ll7+GC kk6<<

CFIIdvgfIM z)(//
;M(MbFg #q 	 	2 6!hw/2%8Xg.s3=ALL
E,/AB  
 KKV\\*KKvzz*GGEN$$(8(8'9%GHHKKcwK7IIcNOr2   c                    US   S:X  a  S[        US   5       S3nOSSUS   -
  S S	3nUR                   S
U 3nUR                  U S35      nU R                  (       a-  U R                  R	                  5       S:w  a  U R                  SS9OU nUR                  USS9  UR                  U S35      nU R                  USS9  XW4$ )z4Save hotspots as both GeoJSON and Shapefile formats.r   r   	hotspots_r   km2hotspots_topr   r   pctr5   z.geojsoni  )epsgGeoJSON)driverz.shpzESRI Shapefile)rM   r9   r   rn   to_epsgr   to_file)r   r3   re   suffix	base_namegeojson_pathhotspots_wgs84shp_paths           r   save_outputsr   )  s    6!S'!234C8HW$5 5c:#>##$AfX.I ((I;h)?@L7C7G7GLL\L\LdLdLfjnLn\((d(3  uAN<	: $$	{$%78H*:;!!r2   c                  n   [         R                  " SS9n U R                  SSS9  U R                  SSS[         S	3S
9  U R                  SS[         R                  S
9  U R                  SSS[
         S	3S
9  U R                  SS[         R                  S
9  U R                  SSSSS9  U R                  5       $ )NzWExtract smooth hotspot contours from density GeoTIFFs using continuous raster analysis.)descriptionlocationz/Location name (e.g., alabama, egypt, thailand).)helpz--target-areatarget_areaz>Target hotspot area in square kilometers. Defaults to env var r   )destr   z--HOTSPOT_TARGET_AREA_KM2target_area_aliasz--target-percentiletarget_percentilezITarget percentile threshold (e.g., 90 for top 10%%). Defaults to env var z--HOTSPOT_TARGET_PERCENTILEtarget_percentile_aliasz--extra-patternr'   r*   z1Additional glob pattern(s) for locating GeoTIFFs.)actionr   r   )argparseArgumentParseradd_argumentr   SUPPRESSr   
parse_args)parsers    r   r   r   >  s    $$mF 
)Z[
##6"7q:	   #   
  ##<"=Q@	   %&  
 @	   r2   c            	      2   [        5       n U R                  R                  5       nU R                  b  U R                  OU R                  nU R
                  b  U R
                  OU R                  n [        X#5      n[        S5      S-  U-  nUR                  5       (       a  UnO[        S5      U-  nUR                  5       (       d  [        SU 35      e[        XpR                  5      nU(       d  [        SU 35      e[        S[        U5       SUR!                  5        S35        [        S	5        U GH=  n	[        S
U	R"                   35         [%        U	5      u  pp [)        XU5      n [+        XX5      nUR,                  (       a  [        S5        Mc  [        S[        U5       S35        US   R/                  5       nSUR0                  ;   a  US   R/                  5       OSn[        SUS S35        US:  a  [        SUS S35        [3        XXX5      n[5        XU5      u  nn[        SUR"                   35        [        SUR"                   35        [        SUR"                   35        GM@     [        S5        g ! [         a  n[        SU 35      UeS nAff = f! [&         a  n[        SU 35         S nAGM  S nAff = f! [         a  n[        SU 35         S nAGM  S nAff = f! [&         a  n[        SU 35         S nAGM  S nAff = f)Nu   ❌ outputgermanyu"   ❌ Location directory not found: u$   ⚠️ No heatmap GeoTIFFs found in u   🎯 Processing z heatmaps for z...u:   🔬 Using continuous raster analysis with smooth contoursu	   
🗺️ u!      ⚠️ Could not load raster: u
      ⚠️ u&      ⚠️ Could not extract contours: u!      ⚠️ No hotspots identified.u      ✅ Found z hotspot region(s)rj   rk   r   u      📍 Total area: rF   r   u      📏 Total perimeter: z kmu      💾 Saved overlay: u      💾 Saved GeoJSON: u      💾 Saved Shapefile: u(   
✅ Smooth contour extraction complete.)r   r   lowerr   r   r   r   r    r   
SystemExitr   existsr1   r*   rN   rJ   
capitalizer;   r   r{   rf   r   r   r_   rl   r   r   )argsr   r   pct_argre   errgermany_dirr)   heatmap_rastersr3   rO   r   r   rP   rZ   hotspots
total_areatotal_perimeterpng_pathr   r   s                        r   mainr  h  s   <D}}""$H#'#3#3#?tTE[E[H(,(>(>(Jd$$PTPlPlG0*8=
 x.9,x7K"H~0  =l^LMM*<9L9LMO?~NOO	S12.ATATAV@WWZ
[\	FH&
;++,-.	2=k2J/D*	+DXFI	3DY[H
 >>56c(m_,>?@j)--/
<JhN^N^<^(>2668de$Z$4E:;Q-oc-B#FG !F[!-hX!Nh'78'(9(9':;<)(--9:Q 'T 

56}  04u&C/04  	5cU;<	  	Jse$%	  	:3%@A	s`   0J J$!K.K2
J!JJ!$
K.KK
K/K**K/2
L<LL__main__)N)'__doc__r   mathr   pathlibr   	geopandasrw   matplotlib.pyplotpyplotr   numpyrH   r   rasterio.featuresr   shapely.geometryr   r   r   shapely.opsr   skimager	   r   r   r    r1   r@   r[   rc   rf   r   r   r   r   r   r  __name__ r2   r   <module>r     s   
   	      $ 9 9 # / 7 5p"($ (6*G?DD(d ($N"*'TG7T zF r2   