=== versions === geopandas 0.13.2 | shapely 2.0.7 | GEOS 3.11.4 === test data === towns used as cluster centres: 48 points: 202,062 grid cells: 9,255 regions: 43 id category value geometry 0 0 burglary 134.29 POINT (330043.351 657750.024) 1 1 criminal damage 101.07 POINT (389617.801 296168.657) 2 2 burglary 146.95 POINT (438262.667 513892.769) 3 3 vehicle 35.88 POINT (350664.759 393165.710) 4 4 burglary 85.89 POINT (203502.102 871590.272) === point-in-polygon === naive loop (2,000 points) 0.393 s -> extrapolated to all points 39.7 s STRtree.query, all points 0.130 s gpd.sjoin, all points 0.246 s matched 202,062 of 202,062 points; all three methods agree sjoin speed-up over naive loop: ~161x cell_id region n_points 1532 1532 5-1 3137 1593 1593 5-1 3057 1531 1531 5-1 2935 1592 1592 5-1 2873 1533 1533 5-1 2235 === buffers === buffer 500 m, quad_segs=16 (default) 0.442 s buffer 500 m, quad_segs=4 0.318 s vertices per buffer: 65 vs 17; area error of quad_segs=4: 2.55% === overlay === buffer + unary_union (20,000 x 1 km) 1.761 s merged into 3,585 hotspot polygons overlay(grid, zones, 'intersection') 0.470 s 8,373 intersection pieces; 659 cells are more than half covered === dissolve === dissolve(by='region') [unary_union] 0.197 s coverage_union_all per region 0.035 s 43 regions; max area difference between methods: 0.000000 m² === file formats === size_MB write_s read_s bbox_read_s bbox_rows format Shapefile 30.3 2.32 1.31 1.430 9006 GeoPackage 23.7 0.71 0.50 0.057 9006 FlatGeobuf 29.6 1.58 1.01 0.080 9006 GeoParquet 5.4 0.17 0.17 0.180 9006 figures written to output