15
15
using System . Threading ;
16
16
using System . Threading . Tasks ;
17
17
using GeoChatter . Core . Common . Extensions ;
18
+ using CefSharp . DevTools . Audits ;
18
19
19
20
namespace GeoChatter . Core . Helpers
20
21
{
@@ -427,10 +428,73 @@ public static Coordinates GetRandomPointWithinBoundBox(Feature feature)
427
428
/// Get given <paramref name="countryNameOrCode"/>'s <see cref="GeoJson"/>
428
429
/// </summary>
429
430
/// <param name="countryNameOrCode">Country name or code</param>
431
+ /// <param name="feature">Feature found</param>
430
432
/// <returns></returns>
431
- public static GeoJson GetCountry ( string countryNameOrCode )
433
+ public static GeoJson GetCountry ( string countryNameOrCode , out Feature feature )
432
434
{
433
- return BorderData . FirstOrDefault ( g => g . features . Any ( f => f . properties . shapeName == countryNameOrCode || f . properties . shapeISO == countryNameOrCode || f . properties . shapeGroup == countryNameOrCode ) ) ;
435
+ feature = null ;
436
+ if ( string . IsNullOrWhiteSpace ( countryNameOrCode ) )
437
+ {
438
+ return null ;
439
+ }
440
+
441
+ countryNameOrCode = countryNameOrCode . Trim ( ) ;
442
+ GeoJson g = null ;
443
+ List < Feature > feats = new List < Feature > ( ) ;
444
+
445
+ foreach ( var geo in BorderData )
446
+ {
447
+ foreach ( var feat in geo . features )
448
+ {
449
+ if ( feat . properties . shapeName . ToUpperInvariant ( ) == countryNameOrCode . ToUpperInvariant ( )
450
+ || feat . properties . shapeName . ReplaceDefault ( " " , "" ) . ToUpperInvariant ( ) == countryNameOrCode . ToUpperInvariant ( ) )
451
+ {
452
+ feature = feat ;
453
+ return geo ;
454
+ }
455
+ else if ( feat . properties . shapeISO . ToUpperInvariant ( ) == countryNameOrCode . ToUpperInvariant ( )
456
+ || feat . properties . shapeISO . ReplaceDefault ( "-" , "" ) . ToUpperInvariant ( ) == countryNameOrCode . ToUpperInvariant ( ) )
457
+ {
458
+ if ( feat . properties . shapeGroup != feat . properties . shapeISO )
459
+ {
460
+ feature = feat ;
461
+ return geo ;
462
+ }
463
+ else if ( g == null || g == geo )
464
+ {
465
+ feats . Add ( feat ) ;
466
+ g = geo ;
467
+ }
468
+ else
469
+ {
470
+ feats . Clear ( ) ;
471
+ feats . Add ( feat ) ;
472
+ g = geo ;
473
+ }
474
+ }
475
+ else if ( feat . properties . shapeGroup . ToUpperInvariant ( ) == countryNameOrCode . ToUpperInvariant ( )
476
+ || feat . properties . shapeGroup . ReplaceDefault ( "-" , "" ) . ToUpperInvariant ( ) == countryNameOrCode . ToUpperInvariant ( ) )
477
+ {
478
+ if ( g == null || g == geo )
479
+ {
480
+ feats . Add ( feat ) ;
481
+ g = geo ;
482
+ }
483
+ else
484
+ {
485
+ feats . Clear ( ) ;
486
+ feats . Add ( feat ) ;
487
+ g = geo ;
488
+ }
489
+ }
490
+ }
491
+ }
492
+
493
+ if ( feats . Count > 0 )
494
+ {
495
+ feature = feats [ random . Next ( feats . Count ) ] ;
496
+ }
497
+ return g ;
434
498
}
435
499
436
500
private static int GetFeatureFactor ( GeoJson geo )
@@ -473,6 +537,20 @@ public static GeoJson GetRandomCountry()
473
537
return GetRandomCountryCacheBorder [ i ] ;
474
538
}
475
539
540
+ /// <summary>
541
+ /// Get GeoJson of given country code or name
542
+ /// </summary>
543
+ /// <returns></returns>
544
+ public static GeoJson GetGeoJSONOf ( string codeOrName )
545
+ {
546
+ codeOrName = codeOrName ? . Trim ( ) . ToUpperInvariant ( ) ;
547
+ return BorderData
548
+ . FirstOrDefault ( b => b . features
549
+ . Any ( f => f . properties . shapeGroup == codeOrName
550
+ || f . properties . shapeISO == codeOrName
551
+ || f . properties . shapeName . ToUpperInvariant ( ) == codeOrName ) ) ;
552
+ }
553
+
476
554
/// <summary>
477
555
/// Get a random feature from given country
478
556
/// </summary>
@@ -485,12 +563,12 @@ public static Feature GetRandomFeature(GeoJson geo)
485
563
}
486
564
487
565
/// <summary>
488
- /// See <see cref="GetRandomPointCloseOrWithinAPolygon (out Feature)"/>
566
+ /// See <see cref="GetRandomPointCloseOrWithinARandomPolygon (out Feature)"/>
489
567
/// </summary>
490
568
/// <returns></returns>
491
- public static Coordinates GetRandomPointCloseOrWithinAPolygon ( )
569
+ public static Coordinates GetRandomPointCloseOrWithinARandomPolygon ( )
492
570
{
493
- return GetRandomPointCloseOrWithinAPolygon ( out Feature _ ) ;
571
+ return GetRandomPointCloseOrWithinARandomPolygon ( out Feature _ ) ;
494
572
}
495
573
496
574
/// <summary>
@@ -499,12 +577,246 @@ public static Coordinates GetRandomPointCloseOrWithinAPolygon()
499
577
/// </summary>
500
578
/// <param name="feature">Randomly picked feature</param>
501
579
/// <returns></returns>
502
- public static Coordinates GetRandomPointCloseOrWithinAPolygon ( out Feature feature )
580
+ public static Coordinates GetRandomPointCloseOrWithinARandomPolygon ( out Feature feature )
503
581
{
504
582
feature = GetRandomFeature ( GetRandomCountry ( ) ) ;
505
583
return GetRandomPointWithinBoundBox ( feature ) ;
506
584
}
507
585
586
+ /// <summary>
587
+ /// See <see cref="GetRandomPointWithinARandomPolygon(out Feature, int)"/>
588
+ /// </summary>
589
+ /// <returns></returns>
590
+ public static Coordinates GetRandomPointWithinARandomPolygon ( int maxTries = 100 )
591
+ {
592
+ return GetRandomPointWithinARandomPolygon ( out Feature _ , maxTries ) ;
593
+ }
594
+
595
+ /// <summary>
596
+ /// Get a random point within ANY polygon
597
+ /// </summary>
598
+ /// <para>WARNING: This DOES NOT guarantee but tries <paramref name="maxTries"/> amount of times at max to ensure a hit on ANY polygon</para>
599
+ /// <param name="maxTries">Maximum tries in case of misses by <see cref="GetRandomPointCloseOrWithinARandomPolygon(out Feature)"/></param>
600
+ /// <param name="feature">Randomly picked feature</param>
601
+ /// <returns></returns>
602
+ public static Coordinates GetRandomPointWithinARandomPolygon ( out Feature feature , int maxTries = 100 )
603
+ {
604
+ Coordinates co = null ;
605
+ feature = null ;
606
+ int tries = 0 ;
607
+ while ( co == null && tries ++ < maxTries )
608
+ {
609
+ GeoJson g = GetRandomCountry ( ) ;
610
+ co = GetRandomPointWithinBoundBox ( GetRandomFeature ( g ) ) ;
611
+
612
+ GetFeatureHitBy ( new double [ 2 ] { co . Longitude , co . Latitude } , out GeoJson gjhit , out feature , out _ ) ;
613
+
614
+ if ( g == gjhit )
615
+ {
616
+ break ;
617
+ }
618
+ }
619
+ return co ;
620
+ }
621
+
622
+ /// <summary>
623
+ /// See <see cref="GetRandomPointWithin(string, out Feature, int)"/>
624
+ /// </summary>
625
+ /// <returns></returns>
626
+ public static Coordinates GetRandomPointWithin ( string codeOrName , int maxTries = 100 )
627
+ {
628
+ return GetRandomPointWithin ( codeOrName , out Feature _ , maxTries ) ;
629
+ }
630
+
631
+ /// <summary>
632
+ /// Get a random point within a polygon of given <paramref name="codeOrName"/> geojson
633
+ /// </summary>
634
+ /// <param name="codeOrName">Country code or name, <see cref="GetCountry(string)"/></param>
635
+ /// <param name="maxTries">Maximum tries in case of misses by <see cref="GetRandomPointCloseOrWithinARandomPolygon(out Feature)"/></param>
636
+ /// <param name="feature">Randomly picked feature</param>
637
+ /// <returns></returns>
638
+ public static Coordinates GetRandomPointWithin ( string codeOrName , out Feature feature , int maxTries = 100 )
639
+ {
640
+ GeoJson g = GetCountry ( codeOrName , out Feature feat ) ;
641
+ if ( g == null )
642
+ {
643
+ return GetRandomPointCloseOrWithinARandomPolygon ( out feature ) ;
644
+ }
645
+
646
+ Coordinates co = null ;
647
+ feature = null ;
648
+ int tries = 0 ;
649
+ feat ??= GetRandomFeature ( g ) ;
650
+
651
+ while ( tries ++ < maxTries )
652
+ {
653
+ co = GetRandomPointWithinBoundBox ( feat ) ;
654
+
655
+ GetFeatureHitBy ( new double [ 2 ] { co . Longitude , co . Latitude } , out GeoJson gjhit , out feature , out _ ) ;
656
+
657
+ if ( g == gjhit )
658
+ {
659
+ break ;
660
+ }
661
+ else
662
+ {
663
+ co = null ;
664
+ }
665
+ }
666
+ if ( co == null )
667
+ {
668
+ return GetRandomPointCloseOrWithinARandomPolygon ( out feature ) ;
669
+ }
670
+ return co ;
671
+ }
672
+ /// <summary>
673
+ /// Get a random point within given country polygon's bounding box present in current border set
674
+ /// <para>WARNING: This DOES NOT guarantee that the point will be within a polygon</para>
675
+ /// </summary>
676
+ /// <param name="codeOrName">Randomly picked feature</param>
677
+ /// <param name="feature">Randomly picked feature</param>
678
+ /// <returns></returns>
679
+ public static Coordinates GetRandomPointCloseOrWithin ( string codeOrName , out Feature feature )
680
+ {
681
+ feature = GetRandomFeature ( GetGeoJSONOf ( codeOrName ) ) ;
682
+ return GetRandomPointWithinBoundBox ( feature ) ;
683
+ }
684
+
685
+ /// <summary>
686
+ /// Get alpha3 code from alpha2 code or name
687
+ /// </summary>
688
+ /// <param name="codeOrName"></param>
689
+ /// <returns></returns>
690
+ public static string GetAlpha3FromCodeOrName ( string codeOrName )
691
+ {
692
+ if ( string . IsNullOrWhiteSpace ( codeOrName ) )
693
+ {
694
+ return string . Empty ;
695
+ }
696
+
697
+ string match = codeOrName . ToUpperInvariant ( ) ;
698
+ if ( match . Length == 2 )
699
+ match = ISO3166Helper . FromAlpha2 ( match ) ? . Alpha3 ;
700
+ else
701
+ match = ISO3166Helper . Collection . FirstOrDefault ( c => c . Name . ToUpperInvariant ( ) == match ) ? . Alpha3 ;
702
+
703
+ return match ?? codeOrName ;
704
+ }
705
+
706
+ private static string GetNameFromRandomGuessArg ( string arg )
707
+ {
708
+ if ( string . IsNullOrWhiteSpace ( arg ) )
709
+ {
710
+ return string . Empty ;
711
+ }
712
+
713
+ string [ ] splt = arg . Split ( ':' , StringSplitOptions . RemoveEmptyEntries | StringSplitOptions . TrimEntries ) ;
714
+ if ( splt . Length >= 1 )
715
+ {
716
+ return splt [ 0 ] ;
717
+ }
718
+ else
719
+ {
720
+ return arg ;
721
+ }
722
+ }
723
+
724
+ private static double GetWeightFromRandomGuessArg ( string arg )
725
+ {
726
+ if ( string . IsNullOrWhiteSpace ( arg ) )
727
+ {
728
+ return 0D ;
729
+ }
730
+
731
+ string [ ] splt = arg . Split ( ':' , StringSplitOptions . RemoveEmptyEntries | StringSplitOptions . TrimEntries ) ;
732
+ if ( splt . Length <= 1 )
733
+ {
734
+ return 1D ;
735
+ }
736
+ else
737
+ {
738
+ return splt [ 1 ] . ParseAsDouble ( 1 ) ;
739
+ }
740
+ }
741
+ /// <summary>
742
+ /// Get random coordinates around given areas
743
+ /// </summary>
744
+ /// <param name="randomGuessQuery"><code>targetCountryNameOrCode</code> OR <code>target1:relativeProbability1 target2:relativeProbability2 ...</code></param>
745
+ /// <returns></returns>
746
+ public static Coordinates GetRandomCoordinateFromRandomGuessQuery ( string randomGuessQuery )
747
+ {
748
+ Coordinates rand = null ;
749
+ try
750
+ {
751
+ if ( string . IsNullOrWhiteSpace ( randomGuessQuery ) )
752
+ {
753
+ return rand ;
754
+ }
755
+
756
+ // TODO: Refactor
757
+ string [ ] countryArgs = randomGuessQuery
758
+ . Split ( ' ' , StringSplitOptions . RemoveEmptyEntries | StringSplitOptions . TrimEntries )
759
+ . OrderBy ( GetWeightFromRandomGuessArg )
760
+ . ToArray ( ) ;
761
+ if ( countryArgs . Length > 0 )
762
+ {
763
+
764
+ if ( countryArgs . Length == 1 )
765
+ {
766
+ string match = GetAlpha3FromCodeOrName ( GetNameFromRandomGuessArg ( countryArgs [ 0 ] ) ) ;
767
+ rand = GetRandomPointWithin ( match ) ;
768
+ }
769
+ else
770
+ {
771
+ List < string > matches = countryArgs
772
+ . Select ( GetNameFromRandomGuessArg )
773
+ . ToList ( ) ;
774
+
775
+ List < double > probs = countryArgs
776
+ . Select ( GetWeightFromRandomGuessArg )
777
+ . ToList ( ) ;
778
+
779
+ double totalProb = probs . Sum ( ) ;
780
+
781
+ int i = matches . Count - 1 ;
782
+ double r = random . NextDouble ( ) * totalProb ;
783
+ while ( r > 0 && i >= 0 )
784
+ {
785
+ Coordinates old = rand ;
786
+ string match = GetAlpha3FromCodeOrName ( matches [ i ] ) ;
787
+ rand = GetRandomPointWithin ( match ) ;
788
+
789
+ if ( rand == null )
790
+ {
791
+ rand = old ;
792
+ }
793
+
794
+ r -= probs [ i -- ] ;
795
+ }
796
+
797
+ if ( rand == null )
798
+ {
799
+ foreach ( string m in matches )
800
+ {
801
+ string match = GetAlpha3FromCodeOrName ( m ) ;
802
+ rand = GetRandomPointWithin ( match ) ;
803
+ if ( rand != null )
804
+ {
805
+ break ;
806
+ }
807
+ }
808
+ }
809
+ }
810
+ }
811
+ return rand ;
812
+ }
813
+ catch ( Exception ex )
814
+ {
815
+ logger . Error ( ex . Summarize ( ) ) ;
816
+ return rand ;
817
+ }
818
+ }
819
+
508
820
/// <summary>
509
821
/// Get location information of given coordinates <paramref name="point"/>
510
822
/// </summary>
0 commit comments