Refactor: extract geocoder

This commit is contained in:
Ming Ming 2023-08-28 00:54:18 +08:00
parent 0ca85f2598
commit fd5b7f7e5b
22 changed files with 197 additions and 92 deletions

View file

@ -1,7 +1,7 @@
import 'package:drift/drift.dart';
import 'package:nc_photos/account.dart' as app;
import 'package:nc_photos/entity/sqlite/database.dart';
import 'package:nc_photos/location_util.dart' as location_util;
import 'package:np_geocoder/np_geocoder.dart';
import 'package:np_string/np_string.dart';
enum FilesQueryMode {
@ -181,11 +181,11 @@ class FilesQueryBuilder {
var clause = db.imageLocations.name.like(_byLocation!) |
db.imageLocations.admin1.like(_byLocation!) |
db.imageLocations.admin2.like(_byLocation!);
final countryCode = location_util.nameToAlpha2Code(_byLocation!.toCi());
final countryCode = nameToAlpha2Code(_byLocation!.toCi());
if (countryCode != null) {
clause = clause | db.imageLocations.countryCode.equals(countryCode);
} else if (_byLocation!.length == 2 &&
location_util.alpha2CodeToName(_byLocation!.toUpperCase()) != null) {
alpha2CodeToName(_byLocation!.toUpperCase()) != null) {
clause = clause |
db.imageLocations.countryCode.equals(_byLocation!.toUpperCase());
}

View file

@ -0,0 +1,15 @@
import 'package:nc_photos/entity/file.dart';
import 'package:np_geocoder/np_geocoder.dart';
extension ReverseGeocoderExtension on ReverseGeocoderLocation {
ImageLocation toImageLocation() {
return ImageLocation(
name: name,
latitude: latitude,
longitude: longitude,
countryCode: countryCode,
admin1: admin1,
admin2: admin2,
);
}
}

View file

@ -9,9 +9,9 @@ import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/entity/file_util.dart' as file_util;
import 'package:nc_photos/entity/pref.dart';
import 'package:nc_photos/event/event.dart';
import 'package:nc_photos/reverse_geocoder.dart';
import 'package:nc_photos/use_case/update_missing_metadata.dart';
import 'package:np_codegen/np_codegen.dart';
import 'package:np_geocoder/np_geocoder.dart';
import 'package:to_string/to_string.dart';
part 'metadata_task_manager.g.dart';

View file

@ -2,13 +2,10 @@ import 'dart:io' as dart;
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:nc_photos/entity/sqlite/database.dart' as sql;
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
import 'package:path/path.dart' as path_lib;
import 'package:path_provider/path_provider.dart';
import 'package:sqlite3/common.dart';
import 'package:sqlite3/sqlite3.dart';
import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart' as sql;
Future<Map<String, dynamic>> getSqliteConnectionArgs() async {
@ -40,28 +37,6 @@ Future<void> applyWorkaroundToOpenSqlite3OnOldAndroidVersions() {
return sql.applyWorkaroundToOpenSqlite3OnOldAndroidVersions();
}
Future<CommonDatabase> openRawSqliteDbFromAsset(
String assetRelativePath,
String outputFilename, {
bool isReadOnly = false,
}) async {
final dbFolder = await getApplicationDocumentsDirectory();
final file = dart.File(path_lib.join(dbFolder.path, outputFilename));
if (!await file.exists()) {
// copy file from assets
final blob = await rootBundle.load("assets/$assetRelativePath");
final buffer = blob.buffer;
await file.writeAsBytes(
buffer.asUint8List(blob.offsetInBytes, blob.lengthInBytes),
flush: true,
);
}
return sqlite3.open(
file.path,
mode: isReadOnly ? OpenMode.readOnly : OpenMode.readWriteCreate,
);
}
Future<dynamic> exportSqliteDb(sql.SqliteDb db) async {
final dir = await getApplicationDocumentsDirectory();
final file = dart.File(path_lib.join(dir.path, "export.sqlite"));

View file

@ -19,11 +19,11 @@ import 'package:nc_photos/entity/pref.dart';
import 'package:nc_photos/event/event.dart';
import 'package:nc_photos/event/native_event.dart';
import 'package:nc_photos/language_util.dart' as language_util;
import 'package:nc_photos/reverse_geocoder.dart';
import 'package:nc_photos/use_case/update_missing_metadata.dart';
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
import 'package:np_async/np_async.dart';
import 'package:np_codegen/np_codegen.dart';
import 'package:np_geocoder/np_geocoder.dart';
part 'service.g.dart';

View file

@ -6,8 +6,8 @@ import 'package:nc_photos/entity/file_descriptor.dart';
import 'package:nc_photos/entity/sqlite/database.dart' as sql;
import 'package:nc_photos/entity/sqlite/files_query_builder.dart' as sql;
import 'package:nc_photos/entity/sqlite/type_converter.dart';
import 'package:nc_photos/location_util.dart' as location_util;
import 'package:nc_photos/object_extension.dart';
import 'package:np_geocoder/np_geocoder.dart';
class ListLocationFile {
ListLocationFile(this._c) : assert(require(_c));
@ -29,8 +29,7 @@ class ListLocationFile {
});
return q.build();
});
if (place == null ||
location_util.alpha2CodeToName(countryCode) == place) {
if (place == null || alpha2CodeToName(countryCode) == place) {
// some places in the DB have the same name as the country, in such
// cases, we return all photos from the country
query.where(db.imageLocations.countryCode.equals(countryCode));

View file

@ -4,8 +4,8 @@ import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart';
import 'package:nc_photos/di_container.dart';
import 'package:nc_photos/entity/sqlite/database.dart' as sql;
import 'package:nc_photos/location_util.dart' as location_util;
import 'package:np_codegen/np_codegen.dart';
import 'package:np_geocoder/np_geocoder.dart';
import 'package:to_string/to_string.dart';
part 'list_location_group.g.dart';
@ -141,7 +141,7 @@ class ListLocationGroup {
await countryCodeQ.map((r) {
final cc = r.read(db.imageLocations.countryCode)!;
return LocationGroup(
location_util.alpha2CodeToName(cc) ?? cc,
alpha2CodeToName(cc) ?? cc,
cc,
r.read(count)!,
r.read(db.files.fileId)!,

View file

@ -9,13 +9,14 @@ import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/event/event.dart';
import 'package:nc_photos/exception.dart';
import 'package:nc_photos/exception_event.dart';
import 'package:nc_photos/reverse_geocoder.dart';
import 'package:nc_photos/geocoder_util.dart';
import 'package:nc_photos/use_case/get_file_binary.dart';
import 'package:nc_photos/use_case/load_metadata.dart';
import 'package:nc_photos/use_case/scan_missing_metadata.dart';
import 'package:nc_photos/use_case/update_property.dart';
import 'package:np_codegen/np_codegen.dart';
import 'package:np_common/or_null.dart';
import 'package:np_geocoder/np_geocoder.dart';
part 'update_missing_metadata.g.dart';

View file

@ -1,6 +1,5 @@
import 'package:drift/drift.dart';
import 'package:drift/wasm.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:http/http.dart' as http;
import 'package:nc_photos/entity/sqlite/database.dart' as sql;
import 'package:sqlite3/wasm.dart';
@ -36,33 +35,6 @@ Future<void> applyWorkaroundToOpenSqlite3OnOldAndroidVersions() async {
// not supported on web
}
Future<CommonDatabase> openRawSqliteDbFromAsset(
String assetRelativePath,
String outputFilename, {
bool isReadOnly = false,
}) async {
final response = await http.get(Uri.parse("sqlite3.wasm"));
final fs = await IndexedDbFileSystem.open(dbName: "nc-photos");
final sqlite3 = await WasmSqlite3.load(
response.bodyBytes,
SqliteEnvironment(fileSystem: fs),
);
if (!fs.exists("/app-file/$outputFilename")) {
// copy file from assets
final blob = await rootBundle.load("assets/$assetRelativePath");
final buffer = blob.buffer;
fs.createFile("/app-file/$outputFilename");
fs.write(
"/app-file/$outputFilename",
buffer.asUint8List(blob.offsetInBytes, blob.lengthInBytes),
0,
);
await fs.flush();
}
return sqlite3.open("/app-file/$outputFilename");
}
Future<dynamic> exportSqliteDb(sql.SqliteDb db) async {
throw UnimplementedError();
}

View file

@ -19,7 +19,6 @@ import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/entity/file_descriptor.dart';
import 'package:nc_photos/entity/file_util.dart' as file_util;
import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/location_util.dart' as location_util;
import 'package:nc_photos/object_extension.dart';
import 'package:nc_photos/platform/features.dart' as features;
import 'package:nc_photos/set_as_handler.dart';
@ -35,6 +34,7 @@ import 'package:nc_photos/widget/list_tile_center_leading.dart';
import 'package:nc_photos/widget/photo_date_time_edit_dialog.dart';
import 'package:np_codegen/np_codegen.dart';
import 'package:np_common/or_null.dart';
import 'package:np_geocoder/np_geocoder.dart';
import 'package:np_platform_util/np_platform_util.dart';
import 'package:np_string/np_string.dart';
import 'package:np_ui/np_ui.dart';
@ -564,11 +564,11 @@ extension on ImageLocation {
if (countryCode == null) {
return null;
} else if (admin1 == null) {
return location_util.alpha2CodeToName(countryCode!);
return alpha2CodeToName(countryCode!);
} else if (admin2 == null) {
return "$admin1, ${location_util.alpha2CodeToName(countryCode!)}";
return "$admin1, ${alpha2CodeToName(countryCode!)}";
} else {
return "$admin2, $admin1, ${location_util.alpha2CodeToName(countryCode!)}";
return "$admin2, $admin1, ${alpha2CodeToName(countryCode!)}";
}
}
}

View file

@ -782,7 +782,7 @@ packages:
source: hosted
version: "4.8.1"
kdtree:
dependency: "direct main"
dependency: transitive
description:
path: "."
ref: "0.2.0-nc-photos-1"
@ -983,6 +983,13 @@ packages:
relative: true
source: path
version: "0.0.1"
np_geocoder:
dependency: "direct main"
description:
path: "../np_geocoder"
relative: true
source: path
version: "0.0.1"
np_lints:
dependency: "direct dev"
description:

View file

@ -84,10 +84,6 @@ dependencies:
ref: 1.0.0-nc-photos-2
path: library
intl: ^0.17.0
kdtree:
git:
url: https://gitlab.com/nc-photos/kd-tree-dart.git
ref: 0.2.0-nc-photos-1
kiwi: ^4.1.0
latlong2: any
logging: ^1.1.1
@ -107,6 +103,8 @@ dependencies:
path: ../np_common
np_collection:
path: ../np_collection
np_geocoder:
path: ../np_geocoder
np_log:
path: ../np_log
np_math:

30
np_geocoder/.gitignore vendored Normal file
View file

@ -0,0 +1,30 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.packages
build/

10
np_geocoder/.metadata Normal file
View file

@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
channel: stable
project_type: package

View file

@ -0,0 +1 @@
include: package:np_lints/np.yaml

View file

@ -0,0 +1,4 @@
library np_geocoder;
export 'src/location_util.dart';
export 'src/reverse_geocoder.dart';

View file

@ -0,0 +1,29 @@
import 'dart:io' as dart;
import 'package:flutter/services.dart' show rootBundle;
import 'package:path/path.dart' as path_lib;
import 'package:path_provider/path_provider.dart';
import 'package:sqlite3/common.dart';
import 'package:sqlite3/sqlite3.dart';
Future<CommonDatabase> openRawSqliteDbFromAsset(
String assetRelativePath,
String outputFilename, {
bool isReadOnly = false,
}) async {
final dbFolder = await getApplicationDocumentsDirectory();
final file = dart.File(path_lib.join(dbFolder.path, outputFilename));
if (!await file.exists()) {
// copy file from assets
final blob = await rootBundle.load("assets/$assetRelativePath");
final buffer = blob.buffer;
await file.writeAsBytes(
buffer.asUint8List(blob.offsetInBytes, blob.lengthInBytes),
flush: true,
);
}
return sqlite3.open(
file.path,
mode: isReadOnly ? OpenMode.readOnly : OpenMode.readWriteCreate,
);
}

View file

@ -3,10 +3,9 @@ import 'dart:math' as math;
import 'package:collection/collection.dart';
import 'package:kdtree/kdtree.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/mobile/platform.dart'
if (dart.library.html) 'package:nc_photos/web/platform.dart' as platform;
import 'package:np_codegen/np_codegen.dart';
import 'package:np_geocoder/src/native/db_util.dart'
if (dart.library.html) 'package:np_geocoder/src/web/db_util.dart';
import 'package:np_math/np_math.dart';
import 'package:sqlite3/common.dart';
import 'package:to_string/to_string.dart';
@ -109,19 +108,6 @@ class ReverseGeocoder {
late final KDTree _searchTree;
}
extension ReverseGeocoderExtension on ReverseGeocoderLocation {
ImageLocation toImageLocation() {
return ImageLocation(
name: name,
latitude: latitude,
longitude: longitude,
countryCode: countryCode,
admin1: admin1,
admin2: admin2,
);
}
}
class _DatabaseRow {
const _DatabaseRow(this.name, this.latitude, this.longitude, this.countryCode,
this.admin1, this.admin2);
@ -135,7 +121,7 @@ class _DatabaseRow {
}
Future<CommonDatabase> _openDatabase() async {
return platform.openRawSqliteDbFromAsset("cities.sqlite", "cities.sqlite");
return openRawSqliteDbFromAsset("cities.sqlite", "cities.sqlite");
}
KDTree _buildSearchTree(CommonDatabase db) {

View file

@ -10,7 +10,7 @@ extension _$ReverseGeocoderNpLog on ReverseGeocoder {
// ignore: unused_element
Logger get _log => log;
static final log = Logger("reverse_geocoder.ReverseGeocoder");
static final log = Logger("src.reverse_geocoder.ReverseGeocoder");
}
// **************************************************************************
@ -20,6 +20,6 @@ extension _$ReverseGeocoderNpLog on ReverseGeocoder {
extension _$ReverseGeocoderLocationToString on ReverseGeocoderLocation {
String _$toString() {
// ignore: unnecessary_string_interpolations
return "ReverseGeocoderLocation {name: $name, latitude: ${latitude.toStringAsFixed(3)}, longitude: ${longitude.toStringAsFixed(3)}, countryCode: $countryCode, admin1: $admin1, admin2: $admin2}";
return "ReverseGeocoderLocation {name: $name, latitude: $latitude, longitude: $longitude, countryCode: $countryCode, admin1: $admin1, admin2: $admin2}";
}
}

View file

@ -0,0 +1,30 @@
import 'package:flutter/services.dart' show rootBundle;
import 'package:http/http.dart' as http;
import 'package:sqlite3/wasm.dart';
Future<CommonDatabase> openRawSqliteDbFromAsset(
String assetRelativePath,
String outputFilename, {
bool isReadOnly = false,
}) async {
final response = await http.get(Uri.parse("sqlite3.wasm"));
final fs = await IndexedDbFileSystem.open(dbName: "nc-photos");
final sqlite3 = await WasmSqlite3.load(
response.bodyBytes,
SqliteEnvironment(fileSystem: fs),
);
if (!fs.exists("/app-file/$outputFilename")) {
// copy file from assets
final blob = await rootBundle.load("assets/$assetRelativePath");
final buffer = blob.buffer;
fs.createFile("/app-file/$outputFilename");
fs.write(
"/app-file/$outputFilename",
buffer.asUint8List(blob.offsetInBytes, blob.lengthInBytes),
0,
);
await fs.flush();
}
return sqlite3.open("/app-file/$outputFilename");
}

48
np_geocoder/pubspec.yaml Normal file
View file

@ -0,0 +1,48 @@
name: np_geocoder
description: A new Flutter package project.
version: 0.0.1
homepage:
publish_to: none
environment:
sdk: '>=2.19.6 <3.0.0'
flutter: ">=3.3.0"
dependencies:
flutter:
sdk: flutter
collection: ^1.15.0
http: ^0.13.5
kdtree:
git:
url: https://gitlab.com/nc-photos/kd-tree-dart.git
ref: 0.2.0-nc-photos-1
logging: ^1.1.1
np_codegen:
path: ../codegen
np_collection:
path: ../np_collection
np_math:
path: ../np_math
np_string:
path: ../np_string
path: ^1.8.0
path_provider: ^2.0.15
sqlite3: ^1.11.1
to_string:
git:
url: https://gitlab.com/nkming2/dart-to-string
ref: to_string-1.0.0
path: to_string
dev_dependencies:
build_runner: ^2.2.1
np_codegen_build:
path: ../codegen_build
np_lints:
path: ../np_lints
to_string_build:
git:
url: https://gitlab.com/nkming2/dart-to-string
ref: to_string_build-1.0.0
path: to_string_build