@@ -11247,3 +11247,219 @@ def pmtiles_metadata(input_file: str) -> Dict[str, Union[str, int, List[str]]]:
11247
11247
metadata ["center" ] = header ["center" ]
11248
11248
metadata ["bounds" ] = header ["bounds" ]
11249
11249
return metadata
11250
+
11251
+
11252
+ def raster_to_vector (source , output , simplify_tolerance = None , dst_crs = None , open_args = {}, ** kwargs ):
11253
+ """Vectorize a raster dataset.
11254
+
11255
+ Args:
11256
+ source (str): The path to the tiff file.
11257
+ output (str): The path to the vector file.
11258
+ simplify_tolerance (float, optional): The maximum allowed geometry displacement.
11259
+ The higher this value, the smaller the number of vertices in the resulting geometry.
11260
+ """
11261
+ import rasterio
11262
+ import shapely
11263
+ import geopandas as gpd
11264
+ from rasterio import features
11265
+
11266
+ with rasterio .open (source , ** open_args ) as src :
11267
+ band = src .read ()
11268
+
11269
+ mask = band != 0
11270
+ shapes = features .shapes (band , mask = mask , transform = src .transform )
11271
+
11272
+ fc = [
11273
+ {"geometry" : shapely .geometry .shape (shape ), "properties" : {"value" : value }}
11274
+ for shape , value in shapes
11275
+ ]
11276
+ if simplify_tolerance is not None :
11277
+ for i in fc :
11278
+ i ["geometry" ] = i ["geometry" ].simplify (tolerance = simplify_tolerance )
11279
+
11280
+ gdf = gpd .GeoDataFrame .from_features (fc )
11281
+ if src .crs is not None :
11282
+ gdf .set_crs (crs = src .crs , inplace = True )
11283
+
11284
+ if dst_crs is not None :
11285
+ gdf = gdf .to_crs (dst_crs )
11286
+
11287
+ gdf .to_file (output , ** kwargs )
11288
+
11289
+
11290
+ def overlay_images (
11291
+ image1 ,
11292
+ image2 ,
11293
+ alpha = 0.5 ,
11294
+ backend = "TkAgg" ,
11295
+ height_ratios = [10 , 1 ],
11296
+ show_args1 = {},
11297
+ show_args2 = {},
11298
+ ):
11299
+ """Overlays two images using a slider to control the opacity of the top image.
11300
+
11301
+ Args:
11302
+ image1 (str | np.ndarray): The first input image at the bottom represented as a NumPy array or the path to the image.
11303
+ image2 (_type_): The second input image on top represented as a NumPy array or the path to the image.
11304
+ alpha (float, optional): The alpha value of the top image. Defaults to 0.5.
11305
+ backend (str, optional): The backend of the matplotlib plot. Defaults to "TkAgg".
11306
+ height_ratios (list, optional): The height ratios of the two subplots. Defaults to [10, 1].
11307
+ show_args1 (dict, optional): The keyword arguments to pass to the imshow() function for the first image. Defaults to {}.
11308
+ show_args2 (dict, optional): The keyword arguments to pass to the imshow() function for the second image. Defaults to {}.
11309
+
11310
+ """
11311
+ import sys
11312
+ import matplotlib
11313
+ import matplotlib .pyplot as plt
11314
+ import matplotlib .widgets as mpwidgets
11315
+
11316
+ if "google.colab" in sys .modules :
11317
+ backend = "inline"
11318
+ print (
11319
+ "The TkAgg backend is not supported in Google Colab. The overlay_images function will not work on Colab."
11320
+ )
11321
+ return
11322
+
11323
+ matplotlib .use (backend )
11324
+
11325
+ if isinstance (image1 , str ):
11326
+ if image1 .startswith ("http" ):
11327
+ image1 = download_file (image1 )
11328
+
11329
+ if not os .path .exists (image1 ):
11330
+ raise ValueError (f"Input path { image1 } does not exist." )
11331
+
11332
+ if isinstance (image2 , str ):
11333
+ if image2 .startswith ("http" ):
11334
+ image2 = download_file (image2 )
11335
+
11336
+ if not os .path .exists (image2 ):
11337
+ raise ValueError (f"Input path { image2 } does not exist." )
11338
+
11339
+ # Load the two images
11340
+ x = plt .imread (image1 )
11341
+ y = plt .imread (image2 )
11342
+
11343
+ # Create the plot
11344
+ fig , (ax0 , ax1 ) = plt .subplots (2 , 1 , gridspec_kw = {"height_ratios" : height_ratios })
11345
+ img0 = ax0 .imshow (x , ** show_args1 )
11346
+ img1 = ax0 .imshow (y , alpha = alpha , ** show_args2 )
11347
+
11348
+ # Define the update function
11349
+ def update (value ):
11350
+ img1 .set_alpha (value )
11351
+ fig .canvas .draw_idle ()
11352
+
11353
+ # Create the slider
11354
+ slider0 = mpwidgets .Slider (ax = ax1 , label = "alpha" , valmin = 0 , valmax = 1 , valinit = alpha )
11355
+ slider0 .on_changed (update )
11356
+
11357
+ # Display the plot
11358
+ plt .show ()
11359
+
11360
+
11361
+ def blend_images (
11362
+ img1 ,
11363
+ img2 ,
11364
+ alpha = 0.5 ,
11365
+ output = False ,
11366
+ show = True ,
11367
+ figsize = (12 , 10 ),
11368
+ axis = "off" ,
11369
+ ** kwargs ,
11370
+ ):
11371
+ """
11372
+ Blends two images together using the addWeighted function from the OpenCV library.
11373
+
11374
+ Args:
11375
+ img1 (numpy.ndarray): The first input image on top represented as a NumPy array.
11376
+ img2 (numpy.ndarray): The second input image at the bottom represented as a NumPy array.
11377
+ alpha (float): The weighting factor for the first image in the blend. By default, this is set to 0.5.
11378
+ output (str, optional): The path to the output image. Defaults to False.
11379
+ show (bool, optional): Whether to display the blended image. Defaults to True.
11380
+ figsize (tuple, optional): The size of the figure. Defaults to (12, 10).
11381
+ axis (str, optional): The axis of the figure. Defaults to "off".
11382
+ **kwargs: Additional keyword arguments to pass to the cv2.addWeighted() function.
11383
+
11384
+ Returns:
11385
+ numpy.ndarray: The blended image as a NumPy array.
11386
+ """
11387
+ import cv2
11388
+ import numpy as np
11389
+ import matplotlib .pyplot as plt
11390
+
11391
+ # Resize the images to have the same dimensions
11392
+ if isinstance (img1 , str ):
11393
+ if img1 .startswith ("http" ):
11394
+ img1 = download_file (img1 )
11395
+
11396
+ if not os .path .exists (img1 ):
11397
+ raise ValueError (f"Input path { img1 } does not exist." )
11398
+
11399
+ img1 = cv2 .imread (img1 )
11400
+
11401
+ if isinstance (img2 , str ):
11402
+ if img2 .startswith ("http" ):
11403
+ img2 = download_file (img2 )
11404
+
11405
+ if not os .path .exists (img2 ):
11406
+ raise ValueError (f"Input path { img2 } does not exist." )
11407
+
11408
+ img2 = cv2 .imread (img2 )
11409
+
11410
+ if img1 .dtype == np .float32 :
11411
+ img1 = (img1 * 255 ).astype (np .uint8 )
11412
+
11413
+ if img2 .dtype == np .float32 :
11414
+ img2 = (img2 * 255 ).astype (np .uint8 )
11415
+
11416
+ if img1 .dtype != img2 .dtype :
11417
+ img2 = img2 .astype (img1 .dtype )
11418
+
11419
+ img1 = cv2 .resize (img1 , (img2 .shape [1 ], img2 .shape [0 ]))
11420
+
11421
+ # Blend the images using the addWeighted function
11422
+ beta = 1 - alpha
11423
+ blend_img = cv2 .addWeighted (img1 , alpha , img2 , beta , 0 , ** kwargs )
11424
+
11425
+ if output :
11426
+ array_to_image (blend_img , output , img2 )
11427
+
11428
+ if show :
11429
+ plt .figure (figsize = figsize )
11430
+ plt .imshow (blend_img )
11431
+ plt .axis (axis )
11432
+ plt .show ()
11433
+ else :
11434
+ return blend_img
11435
+
11436
+
11437
+ def regularize (source , output = None , crs = "EPSG:4326" , ** kwargs ):
11438
+ """Regularize a polygon GeoDataFrame.
11439
+
11440
+ Args:
11441
+ source (str | gpd.GeoDataFrame): The input file path or a GeoDataFrame.
11442
+ output (str, optional): The output file path. Defaults to None.
11443
+
11444
+
11445
+ Returns:
11446
+ gpd.GeoDataFrame: The output GeoDataFrame.
11447
+ """
11448
+ import geopandas as gpd
11449
+
11450
+ if isinstance (source , str ):
11451
+ gdf = gpd .read_file (source )
11452
+ elif isinstance (source , gpd .GeoDataFrame ):
11453
+ gdf = source
11454
+ else :
11455
+ raise ValueError ("The input source must be a GeoDataFrame or a file path." )
11456
+
11457
+ polygons = gdf .geometry .apply (lambda geom : geom .minimum_rotated_rectangle )
11458
+ result = gpd .GeoDataFrame (geometry = polygons , data = gdf .drop ("geometry" , axis = 1 ))
11459
+
11460
+ if crs is not None :
11461
+ result .to_crs (crs , inplace = True )
11462
+ if output is not None :
11463
+ result .to_file (output , ** kwargs )
11464
+ else :
11465
+ return result
0 commit comments