LCOV - code coverage report
Current view: top level - src - geocoder_offline_base.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 66 67 98.5 %
Date: 2020-11-18 14:13:03 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:math';
       2             : 
       3             : import 'package:csv/csv.dart';
       4             : import 'package:geocoder_offline/geocoder_offline.dart';
       5             : import 'package:geocoder_offline/src/SearchData.dart';
       6             : import 'package:kdtree/kdtree.dart';
       7             : 
       8             : import 'LocationData.dart';
       9             : 
      10             : class GeocodeData {
      11             :   /// List of possible bearings
      12             :   final List<String> bearings = <String>[
      13             :     'N',
      14             :     'NNE',
      15             :     'NE',
      16             :     'ENE',
      17             :     'E',
      18             :     'ESE',
      19             :     'SE',
      20             :     'SSE',
      21             :     'S',
      22             :     'SSW',
      23             :     'SW',
      24             :     'WSW',
      25             :     'W',
      26             :     'WNW',
      27             :     'NW',
      28             :     'NNW',
      29             :     'N'
      30             :   ];
      31             : 
      32             :   /// Angle that every possible bearings covers
      33             :   final double DIRECTION_RANGE = 22.5;
      34             : 
      35             :   /// String that contains all possible Location
      36             :   final String inputString;
      37             : 
      38             :   /// Field Delimiter in inputString
      39             :   final String fieldDelimiter;
      40             : 
      41             :   /// Text Delimiter in inputString
      42             :   final String textDelimiter;
      43             : 
      44             :   /// End of line character in inputString
      45             :   final String eol;
      46             : 
      47             :   /// Name of column that contains Location name
      48             :   final String featureNameHeader;
      49             : 
      50             :   /// Name of column that contains Location state
      51             :   final String stateHeader;
      52             : 
      53             :   /// Name of column that contains Location latitude
      54             :   final String latitudeHeader;
      55             : 
      56             :   /// Name of column that contains Location longitude
      57             :   final String longitudeHeader;
      58             : 
      59             :   /// Number of nearest result
      60             :   final int numMarkers;
      61             : 
      62             :   KDTree _kdTree;
      63             :   int _featureNameHeaderSN;
      64             :   int _stateHeaderSN;
      65             :   int _latitudeHeaderSN;
      66             :   int _longitudeHeaderSN;
      67             : 
      68           1 :   GeocodeData(this.inputString, this.featureNameHeader, this.stateHeader,
      69             :       this.latitudeHeader, this.longitudeHeader,
      70             :       {this.numMarkers = 1,
      71             :       this.fieldDelimiter = defaultFieldDelimiter,
      72             :       this.textDelimiter = defaultTextDelimiter,
      73             :       this.eol = defaultEol}) {
      74           2 :     var rowsAsListOfValues = const CsvToListConverter().convert(inputString,
      75           1 :         fieldDelimiter: fieldDelimiter,
      76           1 :         textDelimiter: textDelimiter,
      77           1 :         eol: eol,
      78             :         shouldParseNumbers: false);
      79             : 
      80           1 :     _featureNameHeaderSN =
      81           5 :         rowsAsListOfValues[0].indexWhere((x) => x == featureNameHeader);
      82           6 :     _stateHeaderSN = rowsAsListOfValues[0].indexWhere((x) => x == stateHeader);
      83           1 :     _latitudeHeaderSN =
      84           5 :         rowsAsListOfValues[0].indexWhere((x) => x == latitudeHeader);
      85           1 :     _longitudeHeaderSN =
      86           5 :         rowsAsListOfValues[0].indexWhere((x) => x == longitudeHeader);
      87             : 
      88           3 :     if (_featureNameHeaderSN == -1 ||
      89           3 :         _stateHeaderSN == -1 ||
      90           3 :         _latitudeHeaderSN == -1 ||
      91           3 :         _longitudeHeaderSN == -1) {
      92           0 :       throw Exception('Some of header is not find in file');
      93             :     }
      94             : 
      95             :     var locations = rowsAsListOfValues
      96           1 :         .sublist(1)
      97           3 :         .map((model) => LocationData(
      98           2 :             model[_featureNameHeaderSN],
      99           2 :             model[_stateHeaderSN],
     100           4 :             double.tryParse(model[_latitudeHeaderSN].toString()) ?? -1,
     101           4 :             double.tryParse(model[_longitudeHeaderSN].toString()) ?? -1))
     102           3 :         .map((model) => model.toJson())
     103           1 :         .toList();
     104             : 
     105           4 :     _kdTree = KDTree(locations, _distance, ['latitude', 'longitude']);
     106             :   }
     107             : 
     108           1 :   double _distance(location1, location2) {
     109           1 :     var lat1 = location1['latitude'],
     110           1 :         lon1 = location1['longitude'],
     111           1 :         lat2 = location2['latitude'],
     112           1 :         lon2 = location2['longitude'];
     113             : 
     114           1 :     return calculateDistance(lat1, lon1, lat2, lon2);
     115             :   }
     116             : 
     117           1 :   static double _deg2rad(deg) {
     118           2 :     return deg * (pi / 180);
     119             :   }
     120             : 
     121           1 :   static double _rad2deg(rad) {
     122           2 :     return rad * (180 / pi);
     123             :   }
     124             : 
     125           1 :   String _calculateBearing(double lat1, double lon1, double lat2, double lon2) {
     126             :     if (lat1 == null || lon1 == null || lat2 == null || lon2 == null) {
     127             :       return null;
     128             :     }
     129             : 
     130           1 :     var latitude1 = _deg2rad(lat1);
     131           1 :     var latitude2 = _deg2rad(lat2);
     132           2 :     var longDiff = _deg2rad(lon2 - lon1);
     133           3 :     var y = sin(longDiff) * cos(latitude2);
     134           4 :     var x = cos(latitude1) * sin(latitude2) -
     135           5 :         sin(latitude1) * cos(latitude2) * cos(longDiff);
     136             : 
     137           5 :     var degrees = ((_rad2deg(atan2(y, x)) + 360) % 360) - 11.25;
     138             : 
     139           2 :     var index = (degrees ~/ DIRECTION_RANGE);
     140             : 
     141           2 :     return bearings[index];
     142             :   }
     143             : 
     144           1 :   List<LocationResult> search(double latitute, double longitude) {
     145           1 :     var result = <LocationResult>[];
     146           1 :     var point = {'latitude': latitute, 'longitude': longitude};
     147           3 :     var nearest = _kdTree.nearest(point, numMarkers);
     148           1 :     var searchData = SearchData(latitute, longitude);
     149             : 
     150           2 :     nearest.forEach((x) {
     151           2 :       var location = LocationData.fromJson(x[0]);
     152           1 :       double distance = x[1];
     153           1 :       var bearing = _calculateBearing(
     154           2 :           location.latitude, location.longitude, latitute, longitude);
     155           2 :       result.add(LocationResult(location, distance, bearing, searchData));
     156             :     });
     157             : 
     158             :     return result;
     159             :   }
     160             : 
     161           1 :   static double calculateDistance(
     162             :       double latStart, double lonStart, double latEnd, double lonEnd) {
     163             :     var R = 3958.8; // Radius of the earth in miles
     164           2 :     var dLat = _deg2rad(latEnd - latStart); // deg2rad below
     165           2 :     var dLon = _deg2rad(lonEnd - lonStart);
     166           6 :     var a = sin(dLat / 2) * sin(dLat / 2) +
     167           3 :         cos(_deg2rad(latStart)) *
     168           3 :             cos(_deg2rad(latEnd)) *
     169           3 :             sin(dLon / 2) *
     170           2 :             sin(dLon / 2);
     171           5 :     var c = 2 * atan2(sqrt(a), sqrt(1 - a));
     172           1 :     var d = R * c; // Distance in miles
     173             :     return d;
     174             :   }
     175             : }

Generated by: LCOV version 1.15