66using System . ComponentModel ;
77using System . Diagnostics ;
88using System . Linq ;
9+ using System . Threading ;
910using System . Threading . Tasks ;
1011
1112namespace Geohash
@@ -40,26 +41,28 @@ public enum GeohashInclusionCriteria
4041 /// <param name="polygon">The input polygon.</param>
4142 /// <param name="geohashPrecision">The desired geohash precision.</param>
4243 /// <param name="geohashInclusionCriteria">The criteria for including geohashes in the result set.</param>
44+ /// <param name="progress">Allows to report progress</param>
4345 /// <returns>A HashSet containing the geohashes that meet the specified inclusion criteria with the input polygon.</returns>
44- public HashSet < string > GetHashes ( Polygon polygon , int geohashPrecision , GeohashInclusionCriteria geohashInclusionCriteria = GeohashInclusionCriteria . Intersects )
46+
47+ public HashSet < string > GetHashes ( Polygon polygon , int geohashPrecision , GeohashInclusionCriteria geohashInclusionCriteria = GeohashInclusionCriteria . Contains , IProgress < double > progress = null )
4548 {
46- // Determine the envelope of the polygon.
4749 var envelope = polygon . EnvelopeInternal ;
48-
49- // Calculate step size based on geohash precision.
5050 double latStep = 180.0 / Math . Pow ( 2 , ( 5 * geohashPrecision - geohashPrecision % 2 ) / 2 ) ;
51- double lngStep = 180.0 / Math . Pow ( 2 , ( 5 * geohashPrecision + geohashPrecision % 2 ) / 2 - 1 ) ;
52-
53- // Expand envelope by half the step size to ensure boundary geohashes are considered.
51+ double lngStep = 360.0 / Math . Pow ( 2 , ( 5 * geohashPrecision + geohashPrecision % 2 ) / 2 ) ;
5452 envelope . ExpandBy ( latStep / 2 , lngStep / 2 ) ;
5553
56- var geohashes = new ConcurrentBag < string > ( ) ;
57-
54+ HashSet < string > geohashes = new HashSet < string > ( ) ;
5855 bool checkContains = geohashInclusionCriteria == GeohashInclusionCriteria . Contains ;
5956 bool checkIntersects = geohashInclusionCriteria == GeohashInclusionCriteria . Intersects ;
6057
61- // Parallelize the outer latitude loop.
62- Parallel . For ( ( int ) ( envelope . MinY / latStep ) , ( int ) ( envelope . MaxY / latStep ) + 1 , ( latIdx ) =>
58+ int totalSteps = ( int ) Math . Ceiling ( ( envelope . MaxY - envelope . MinY ) / latStep ) ;
59+ int stepsCompleted = 0 ;
60+
61+ // Calculate the size of the work batch for progress reporting
62+ int progressBatchSize = Math . Max ( 1 , totalSteps / 100 ) ;
63+
64+ Parallel . For ( ( int ) ( envelope . MinY / latStep ) , ( int ) ( envelope . MaxY / latStep ) + 1 , ( ) => new HashSet < string > ( ) ,
65+ ( latIdx , state , localGeohashes ) =>
6366 {
6467 double lat = latIdx * latStep ;
6568 Coordinate [ ] coords = new Coordinate [ 5 ] ;
@@ -79,27 +82,37 @@ public HashSet<string> GetHashes(Polygon polygon, int geohashPrecision, GeohashI
7982
8083 var geohashPoly = new Polygon ( new LinearRing ( coords ) ) ;
8184
82- // Check inclusion criteria.
85+
8386 if ( ( checkContains && polygon . Contains ( geohashPoly ) ) ||
8487 ( checkIntersects && polygon . Intersects ( geohashPoly ) ) )
8588 {
86- geohashes . Add ( curGeohash ) ;
89+ localGeohashes . Add ( curGeohash ) ;
8790 }
8891 }
89- } ) ;
90-
91- return geohashes . ToHashSet ( ) ;
92- }
93-
94-
95-
96-
97-
98-
9992
93+ if ( ( latIdx + 1 ) % progressBatchSize == 0 )
94+ {
95+ progress ? . Report ( Math . Min ( 1.0 , ( double ) latIdx / totalSteps ) ) ;
96+ }
10097
98+ return localGeohashes ;
99+ } ,
100+ ( localGeohashes ) =>
101+ {
102+ lock ( geohashes )
103+ {
104+ foreach ( var geohash in localGeohashes )
105+ {
106+ geohashes . Add ( geohash ) ;
107+ }
108+ }
109+ } ) ;
101110
111+ // Final progress update to ensure we reach 100% when done
112+ progress ? . Report ( 1.0 ) ;
102113
114+ return geohashes ;
115+ }
103116
104117 }
105118}
0 commit comments