Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merging to null safety #2

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ok",
"request": "launch",
"type": "dart"
}
]
}
1 change: 1 addition & 0 deletions .wakatime-project
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Falling Machine 12
32 changes: 17 additions & 15 deletions lib/src/extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@ extension GeoJsonSearchX on GeoJson {
/// Given a list of polygons, find which one contains a given point.
///
/// If the point isn't within any of these polygons, return `null`.
Future<List<GeoJsonFeature<GeoJsonPolygon>>> geofenceSearch(
Future<List<GeoJsonFeature<GeoJsonPolygon>?>> geofenceSearch(
List<GeoJsonFeature<GeoJsonPolygon>> geofences,
GeoJsonPoint query,
) async {
final boundingBoxes = getBoundingBoxes(geofences);

final filteredGeofences = [
for (var box in boundingBoxes)
if (box.contains(query.geoPoint.latitude, query.geoPoint.longitude)) box.feature
if (box.contains(query.geoPoint.latitude, query.geoPoint.longitude))
box.feature
];

return await _geofencesContainingPointNaive(filteredGeofences, query);
Expand All @@ -33,14 +34,14 @@ extension GeoJsonSearchX on GeoJson {
///
/// Naive implementation. The geofences should be filtered first using a method such
/// as searching bounding boxes first.
Future<List<GeoJsonFeature<GeoJsonPolygon>>> _geofencesContainingPointNaive(
List<GeoJsonFeature<GeoJsonPolygon>> geofences,
Future<List<GeoJsonFeature<GeoJsonPolygon>?>> _geofencesContainingPointNaive(
List<GeoJsonFeature<GeoJsonPolygon>?> geofences,
GeoJsonPoint query,
) async {
final futures = [
for (var geofence in geofences)
geofencePolygon(
polygon: geofence.geometry,
polygon: geofence!.geometry!,
points: [query],
).then((results) {
/// Nothing found
Expand All @@ -56,18 +57,19 @@ extension GeoJsonSearchX on GeoJson {
}

/// Given a set of geofence polygons, find all of their bounding boxes, and the index at which they were found.
List<GeoBoundingBox> getBoundingBoxes(List<GeoJsonFeature<GeoJsonPolygon>> geofences) {
List<GeoBoundingBox> getBoundingBoxes(
List<GeoJsonFeature<GeoJsonPolygon>> geofences) {
final boundingBoxes = <GeoBoundingBox>[];

for (var i = 0; i <= geofences.length - 1; i++) {
final geofence = geofences[i];

double maxLat;
double minLat;
double maxLong;
double minLong;
double? maxLat;
double? minLat;
double? maxLong;
double? minLong;

for (var geoSerie in geofence.geometry.geoSeries) {
for (var geoSerie in geofence.geometry!.geoSeries) {
for (var geoPoint in geoSerie.geoPoints) {
final lat = geoPoint.latitude;
final long = geoPoint.longitude;
Expand All @@ -88,10 +90,10 @@ extension GeoJsonSearchX on GeoJson {

boundingBoxes.add(GeoBoundingBox(
feature: geofence,
minLat: minLat,
maxLong: maxLong,
maxLat: maxLat,
minLong: minLong,
minLat: minLat!,
maxLong: maxLong!,
maxLat: maxLat!,
minLong: minLong!,
));
}

Expand Down
22 changes: 11 additions & 11 deletions lib/src/geoBoundingBox.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import 'package:geojson/geojson.dart';

class GeoBoundingBox {
/// The polygon bounded by this bounding box
final GeoJsonFeature<GeoJsonPolygon> feature;
final GeoJsonFeature<GeoJsonPolygon>? feature;

final double maxLat;
final double maxLong;
final double minLat;
final double minLong;
final double? maxLat;
final double? maxLong;
final double? minLat;
final double? minLong;

/// A geographical rectangle. Typically used as a bounding box for a polygon
/// for fast search of point-in-multiple-polygon.
Expand All @@ -19,14 +19,14 @@ class GeoBoundingBox {
this.minLong,
});

double get bottom => minLong;
double get left => minLat;
double get right => maxLat;
double get top => maxLong;
double? get bottom => minLong;
double? get left => minLat;
double? get right => maxLat;
double? get top => maxLong;

bool contains(double lat, double long) {
final containsLat = maxLat >= lat && minLat <= lat;
final containsLong = maxLong >= long && minLong <= long;
final containsLat = maxLat! >= lat && minLat! <= lat;
final containsLong = maxLong! >= long && minLong! <= long;
return containsLat && containsLong;
}

Expand Down
60 changes: 38 additions & 22 deletions lib/src/timeZoneFinder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,33 @@ import 'package:timezone_finder/src/extensions.dart';
const int _kOffset = 200;

class TimeZoneFinder {
final _isolates = <Isolate>[];
final _isolates = <Isolate?>[];

void _readZipArchive() {
final bytes = File('lib/assets/timezones.zip').readAsBytesSync();
// void _readZipArchive() {
// final bytes = File('lib/assets/timezones.zip').readAsBytesSync();

// Decode and extract the contents of the Zip archive to disk.
final archive = ZipDecoder().decodeBytes(bytes);
File('lib/assets/' + archive.fileName(0))
..createSync()
..writeAsBytesSync(archive.fileData(0));
}
// // Decode and extract the contents of the Zip archive to disk.
// final archive = ZipDecoder().decodeBytes(bytes);
// File('lib/assets/' + archive.fileName(0))
// ..createSync()
// ..writeAsBytesSync(archive.fileData(0));
// }

/// Finds the time zone name according to the IANA time zone database, for the given [latitude] and [longitude] in degrees.
Future<String> findTimeZoneName(double latitude, double longitude) async {
if (latitude > 90 || latitude < -90 || longitude > 180 || longitude < -180) return null;
Future<String?> findTimeZoneName(double latitude, double longitude) async {
if (latitude > 90 ||
latitude < -90 ||
longitude > 180 ||
longitude < -180) {
return null;
}

final completer = Completer<String>();

var geoPoint = GeoJsonPoint(geoPoint: GeoPoint(latitude: latitude, longitude: longitude));
var geoPoint = GeoJsonPoint(
geoPoint: GeoPoint(latitude: latitude, longitude: longitude));

if (!await File('lib/assets/timezones').exists()) _readZipArchive();
// if (!await File('lib/assets/timezones').exists()) _readZipArchive();

final db = sqlite3.open('lib/assets/timezones');
var resultSet = db.select('SELECT * FROM timezones LIMIT $_kOffset');
Expand All @@ -55,12 +61,15 @@ class TimeZoneFinder {
);

while (resultSet.isNotEmpty) {
final message = _IsolateMessage(resultSet, geoPoint, receiverPort.sendPort);
_isolates.add(await Isolate.spawn(_isolateProcessResultSet, message, onExit: receiverPort.sendPort));
final message =
_IsolateMessage(resultSet, geoPoint, receiverPort.sendPort);
_isolates.add(await Isolate.spawn(_isolateProcessResultSet, message,
onExit: receiverPort.sendPort));
nbIsolate += 1;

offset += _kOffset;
resultSet = db.select('SELECT * FROM timezones LIMIT $_kOffset OFFSET $offset');
resultSet =
db.select('SELECT * FROM timezones LIMIT $_kOffset OFFSET $offset');
}

db.dispose();
Expand All @@ -81,16 +90,15 @@ class TimeZoneFinder {

void _killIsolates() {
for (var isolate in _isolates) {
if (isolate != null) {
isolate.kill(priority: Isolate.immediate);
isolate = null;
}
isolate?.kill(priority: Isolate.immediate);
isolate = null;
}
}

static void _isolateProcessResultSet(_IsolateMessage message) async {
final polygons = <GeoJsonFeature<GeoJsonPolygon>>[];
for (var row in message.resultSet.rows) {
row = row as List<String>;
final feature = GeoJsonFeature<GeoJsonPolygon>();
feature.type = GeoJsonFeatureType.polygon;
feature.properties = {'tzid': row[2]};
Expand All @@ -104,7 +112,15 @@ class TimeZoneFinder {
));
}

feature.geometry = GeoJsonPolygon(geoSeries: [GeoSerie(geoPoints: geoPoints)]);
feature.geometry = GeoJsonPolygon(
geoSeries: List.generate(
feature.geometry!.geoSeries.length,
(index) {
var geoSerie = feature.geometry!.geoSeries[index];
return GeoSerie(name: geoSerie.name, type: geoSerie.type);
},
),
);

polygons.add(feature);
}
Expand All @@ -113,7 +129,7 @@ class TimeZoneFinder {

final result = await geo.geofenceSearch(polygons, message.geoPoint);
if (result.isNotEmpty) {
message.sendPort.send(result.first.properties['tzid']);
message.sendPort.send(result.first!.properties!['tzid']);
}

geo.dispose();
Expand Down
6 changes: 3 additions & 3 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ version: 0.0.1
homepage: https://github.com/mikljacq/timezone_finder

environment:
sdk: '>=2.10.0 <3.0.0'
sdk: '>=2.12.0 <3.0.6'

dependencies:
geojson: ^0.10.0
geopoint: ^0.8.0
geojson: ^1.0.0
geopoint: ^1.0.0
async: ^2.6.1
sqlite3: ^1.1.1
archive: ^3.1.2
Expand Down
7 changes: 4 additions & 3 deletions test/timezone_finder_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:test/test.dart';
import 'package:timezone_finder/timezone_finder.dart';

void main() {
Map<String, Map<String, dynamic>> expectedResults = {
var expectedResults = <String, Map<String, dynamic>>{
'Barcelona': {
'latitude': 41.387048,
'longitude': 2.17413425,
Expand Down Expand Up @@ -62,8 +62,9 @@ void main() {
var expected = expectedResults[entry.key];

test('Timezone Name', () async {
var tzName = await finder.findTimeZoneName(expected['latitude'], expected['longitude']);
expect(tzName, expected['timezone']);
var tzName = await finder.findTimeZoneName(
expected?['latitude'], expected?['longitude']);
expect(tzName, expected?['timezone']);
});
});
}
Expand Down