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

Null-safety with #28 improvement #29

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ ios/.symlinks

#api key files
**/example/android/app/src/main/res/values/strings.xml

.fvm/
.idea/
.metadata
/example.gif
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.1.2-nullsafety.1

* Null safety

## 0.1.2

* Migrate to google_maps_flutter: 0.5.21+3
Expand Down Expand Up @@ -54,4 +58,4 @@

## 0.0.1

* Initial developers preview release.
* Initial developers preview release.
136 changes: 11 additions & 125 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,29 @@ A Flutter package that recreate clustering technique in a [Google Maps](https://

## Developers Preview Status
The package recreate the CLUSTERING technique in a Google Maps.
It's work with data recordered in a dababase SQLite. I use [sqflite](https://pub.dartlang.org/packages/sqflite) (DB TECHNIQUE)
~~It's work with data recordered in a dababase SQLite. I use [sqflite](https://pub.dartlang.org/packages/sqflite) (DB TECHNIQUE)~~
It's work with a list of LatLngAndGeohash object. (MEMORY TECHNIQUE)

## Usage

To use this package, add `clustering_google_maps` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/).

For a better performance, at every zoom variation on the map, the package performs
a specific query on the SQLite database, but you can force update with updateMap() method.
# Use my fork
```
clustering_google_maps:
git:
url: https://github.com/juancki/clustering_google_maps.git
```
For a better performance,~~at every zoom variation on the map, the package performs
a specific query on the SQLite database~~, but you can force update with updateMap() method.

## Getting Started

### DB TECHNIQUE
To work properly, you must have the data of the points saved in a SQLite database.
Latitude, longitude and the string of geohash. These three parameters are necessary for correct operation.
If you have not saved the [GEOHASH](https://pub.dartlang.org/packages/geohash), I suggest you install [GEOHASH](https://pub.dartlang.org/packages/geohash)
plugin and save the value of Geohash in the points table of db.

For this solution you must use the db constructor of ClusteringHelper:

```dart
ClusteringHelper.forDB(...);
```
### ~~DB TECHNIQUE~~

### MEMORY TECHNIQUE

To work properly you must have a list of LatLngAndGeohash object. LatLngAndGeohash is a simple object with Location
and Geohash property, the last is generated automatically; you need only location of the point.
To work properly you must have a list of ~~LatLngAndGeohash~~ MarkerWrapper objects.

For this solution you must use the MEMORY constructor of ClusteringHelper:

Expand All @@ -53,112 +48,3 @@ ClusteringHelper.forMemory(...);
Yuo can customize color, range count and zoom limit of aggregation.
See this class: [AggregationSetup](https://github.com/giandifra/clustering_google_maps/blob/master/lib/src/aggregation_setup.dart).

## Future Implementations

- ~~To further improve performance I am creating a way to perform sql queries only on the latlng bounding box displayed on the map.~~
- ~~I will insert custom marker with number of points.~~

## Quick Example for both solution

```dart
import 'package:example/app_db.dart';
import 'package:example/fake_point.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:clustering_google_maps/clustering_google_maps.dart';

class HomeScreen extends StatefulWidget {
final List<LatLngAndGeohash> list;

HomeScreen({Key key, this.list}) : super(key: key);

@override
_HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
ClusteringHelper clusteringHelper;
final CameraPosition initialCameraPosition =
CameraPosition(target: LatLng(0.000000, 0.000000), zoom: 0.0);

Set<Marker> markers = Set();

void _onMapCreated(GoogleMapController mapController) async {
print("onMapCreated");
clusteringHelper.mapController = mapController;
if (widget.list == null) {
clusteringHelper.database = await AppDatabase.get().getDb();
}
clusteringHelper.updateMap();
}

updateMarkers(Set<Marker> markers) {
setState(() {
this.markers = markers;
});
}

@override
void initState() {
if (widget.list != null) {
initMemoryClustering();
} else {
initDatabaseClustering();
}

super.initState();
}

// For db solution
initDatabaseClustering() {
clusteringHelper = ClusteringHelper.forDB(
dbGeohashColumn: FakePoint.dbGeohash,
dbLatColumn: FakePoint.dbLat,
dbLongColumn: FakePoint.dbLong,
dbTable: FakePoint.tblFakePoints,
updateMarkers: updateMarkers,
aggregationSetup: AggregationSetup(),
);
}

// For memory solution
initMemoryClustering() {
clusteringHelper = ClusteringHelper.forMemory(
list: widget.list,
updateMarkers: updateMarkers,
aggregationSetup: AggregationSetup(markerSize: 150),
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Clustering Example"),
),
body: GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: initialCameraPosition,
markers: markers,
onCameraMove: (newPosition) =>
clusteringHelper.onCameraMove(newPosition, forceUpdate: false),
onCameraIdle: clusteringHelper.onMapIdle,
),
floatingActionButton: FloatingActionButton(
child:
widget.list == null ? Icon(Icons.content_cut) : Icon(Icons.update),
onPressed: () {
if (widget.list == null) {
//Test WHERE CLAUSE
clusteringHelper.whereClause = "WHERE ${FakePoint.dbLat} > 42.6";
}
//Force map update
clusteringHelper.updateMap();
},
),
);
}
}
```

See the `example` directory for a complete sample app.
1 change: 1 addition & 0 deletions example/.flutter-plugins-dependencies
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"google_maps_flutter","path":"/Volumes/Samsung_X5/Users/gianmarcodifrancesco/fvm_cache/versions/stable/.pub-cache/hosted/pub.dartlang.org/google_maps_flutter-2.0.6/","dependencies":[]},{"name":"path_provider","path":"/Volumes/Samsung_X5/Users/gianmarcodifrancesco/fvm_cache/versions/stable/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/","dependencies":[]},{"name":"sqflite","path":"/Volumes/Samsung_X5/Users/gianmarcodifrancesco/fvm_cache/versions/stable/.pub-cache/hosted/pub.dartlang.org/sqflite-2.0.0+3/","dependencies":[]}],"android":[{"name":"flutter_plugin_android_lifecycle","path":"/Volumes/Samsung_X5/Users/gianmarcodifrancesco/fvm_cache/versions/stable/.pub-cache/hosted/pub.dartlang.org/flutter_plugin_android_lifecycle-2.0.2/","dependencies":[]},{"name":"google_maps_flutter","path":"/Volumes/Samsung_X5/Users/gianmarcodifrancesco/fvm_cache/versions/stable/.pub-cache/hosted/pub.dartlang.org/google_maps_flutter-2.0.6/","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"path_provider","path":"/Volumes/Samsung_X5/Users/gianmarcodifrancesco/fvm_cache/versions/stable/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/","dependencies":[]},{"name":"sqflite","path":"/Volumes/Samsung_X5/Users/gianmarcodifrancesco/fvm_cache/versions/stable/.pub-cache/hosted/pub.dartlang.org/sqflite-2.0.0+3/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Volumes/Samsung_X5/Users/gianmarcodifrancesco/fvm_cache/versions/stable/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-2.0.2/","dependencies":[]},{"name":"sqflite","path":"/Volumes/Samsung_X5/Users/gianmarcodifrancesco/fvm_cache/versions/stable/.pub-cache/hosted/pub.dartlang.org/sqflite-2.0.0+3/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Volumes/Samsung_X5/Users/gianmarcodifrancesco/fvm_cache/versions/stable/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-2.0.2/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Volumes/Samsung_X5/Users/gianmarcodifrancesco/fvm_cache/versions/stable/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-2.0.3/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"google_maps_flutter","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2021-07-28 12:40:56.397044","version":"2.2.2"}
1 change: 1 addition & 0 deletions example/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
.pub-cache/
.pub/
build/
.flutter-plugins-dependencies

# Android related
**/android/**/gradle-wrapper.jar
Expand Down
2 changes: 1 addition & 1 deletion example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.juanito21.example"
minSdkVersion 16
minSdkVersion 21
targetSdkVersion 28
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
Expand Down
1 change: 1 addition & 0 deletions example/android/settings_aar.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ':app'
14 changes: 14 additions & 0 deletions example/ios/Flutter/flutter_export_environment.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Volumes/Samsung_X5/Users/gianmarcodifrancesco/fvm_cache/versions/stable"
export "FLUTTER_APPLICATION_PATH=/Volumes/Samsung_X5/Flutter/Plugin/clustering_google_maps-master/example"
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
export "FLUTTER_TARGET=lib/main.dart"
export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=false"
export "TREE_SHAKE_ICONS=false"
export "PACKAGE_CONFIG=.packages"
10 changes: 5 additions & 5 deletions example/lib/app_db.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'package:example/fake_point.dart';
import 'fake_point.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
Expand All @@ -11,15 +11,15 @@ class AppDatabase {

AppDatabase._internal();

Database _database;
Database? _database;

static AppDatabase get() {
return _appDatabase;
}

final _lock = new Lock();

Future<Database> getDb() async {
Future<Database?> getDb() async {
if (_database == null) {
await _lock.synchronized(() async {
// Check again once entering the synchronized block
Expand Down Expand Up @@ -55,8 +55,8 @@ class AppDatabase {
}

Future<void> closeDatabase() async {
if (_database != null && _database.isOpen) {
await _database.close();
if (_database != null && _database!.isOpen) {
await _database?.close();
_database = null;
print("database closed");
}
Expand Down
20 changes: 11 additions & 9 deletions example/lib/fake_point.dart
Original file line number Diff line number Diff line change
@@ -1,34 +1,36 @@
import 'package:geohash/geohash.dart';
import 'package:dart_geohash/dart_geohash.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart' show LatLng;

class FakePoint{
class FakePoint {
static final tblFakePoints = "fakePoints";
static final dbId = "id";
static final dbLat = "latitude";
static final dbLong = "longitude";
static final dbGeohash = "geohash";

LatLng location;
int id;
String geohash;
LatLng? location;
int? id;
String? geohash;

FakePoint({this.location, this.id}) {
final geohasher = GeoHasher();
this.geohash =
Geohash.encode(this.location.latitude, this.location.longitude);
geohasher.encode(this.location!.longitude, this.location!.latitude);
}

FakePoint.fromMap(Map<String, dynamic> map)
: id = map[dbId],
location = LatLng(map[dbLat], map[dbLong]) {
final geohasher = GeoHasher();
this.geohash =
Geohash.encode(this.location.latitude, this.location.longitude);
geohasher.encode(this.location!.longitude, this.location!.latitude);
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data[dbId] = this.id;
data[dbLat] = this.location.latitude;
data[dbLat] = this.location.longitude;
data[dbLat] = this.location!.latitude;
data[dbLat] = this.location!.longitude;
return data;
}
}
43 changes: 24 additions & 19 deletions example/lib/home.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import 'package:example/app_db.dart';
import 'package:example/fake_point.dart';
import 'package:clustering_google_maps/clustering_google_maps.dart'
show AggregationSetup, ClusteringHelper, LatLngAndGeohash, MarkerWrapper;
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:clustering_google_maps/clustering_google_maps.dart' show LatLngAndGeohash,ClusteringHelper,AggregationSetup;

import 'fake_point.dart';

class HomeScreen extends StatefulWidget {
final List<LatLngAndGeohash> list;
final List<Marker> list;

HomeScreen({Key key, this.list}) : super(key: key);
HomeScreen({Key? key, required this.list}) : super(key: key);

@override
_HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
ClusteringHelper clusteringHelper;
late ClusteringHelper clusteringHelper;
final CameraPosition initialCameraPosition =
CameraPosition(target: LatLng(0.000000, 0.000000), zoom: 0.0);

Expand All @@ -23,10 +24,10 @@ class _HomeScreenState extends State<HomeScreen> {
void _onMapCreated(GoogleMapController mapController) async {
print("onMapCreated");
clusteringHelper.mapController = mapController;
if (widget.list == null) {
clusteringHelper.database = await AppDatabase.get().getDb();
}
clusteringHelper.updateMap();
// if (widget.list == null) {
// clusteringHelper.database = await AppDatabase.get().getDb();
// }
clusteringHelper.updateData(widget.list);
}

updateMarkers(Set<Marker> markers) {
Expand All @@ -48,22 +49,26 @@ class _HomeScreenState extends State<HomeScreen> {

// For db solution
initDatabaseClustering() {
clusteringHelper = ClusteringHelper.forDB(
dbGeohashColumn: FakePoint.dbGeohash,
dbLatColumn: FakePoint.dbLat,
dbLongColumn: FakePoint.dbLong,
dbTable: FakePoint.tblFakePoints,
updateMarkers: updateMarkers,
aggregationSetup: AggregationSetup(),
);
// clusteringHelper = ClusteringHelper.forDB(
// dbGeohashColumn: FakePoint.dbGeohash,
// dbLatColumn: FakePoint.dbLat,
// dbLongColumn: FakePoint.dbLong,
// dbTable: FakePoint.tblFakePoints,
// updateMarkers: updateMarkers,
// aggregationSetup: AggregationSetup(),
// );
}

// For memory solution
initMemoryClustering() {
clusteringHelper = ClusteringHelper.forMemory(
list: widget.list,
// list: widget.list!,
updateMarkers: updateMarkers,
aggregationSetup: AggregationSetup(markerSize: 150),
aggregatedCallback: (LatLng center, List<Marker> markers) {
print(center);
print(markers.length);
},
);
}

Expand Down
2 changes: 1 addition & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:example/splash.dart';
import 'splash.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());
Expand Down
Loading