Skip to content
This repository has been archived by the owner on Jul 11, 2024. It is now read-only.

Commit

Permalink
Enabling heatmaps (#171)
Browse files Browse the repository at this point in the history
* heatmaps

* updated flutter version

* flutter downgrade
  • Loading branch information
ggalambas authored Mar 22, 2024
1 parent 771bf59 commit 856344e
Show file tree
Hide file tree
Showing 13 changed files with 194 additions and 1 deletion.
4 changes: 4 additions & 0 deletions packages/atlas/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# atlas

## v1.0.22 (2024-03-21)

- Added support for `Heatmaps`

## v1.0.21 (2024-02-23)

- Added `onMapLoaded` parameter on `Provider`
Expand Down
4 changes: 4 additions & 0 deletions packages/atlas/lib/atlas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export 'src/cluster.dart';
export 'src/cluster_icon.dart';
export 'src/congestion_level_route.dart';
export 'src/device_location.dart';
export 'src/heatmap/heatcolor.dart';
export 'src/heatmap/heatcolor_interpolation.dart';
export 'src/heatmap/heatmap.dart';
export 'src/heatmap/heatpoint.dart';
export 'src/lat_lng.dart';
export 'src/lat_lng_bounds.dart';
export 'src/map_language.dart';
Expand Down
5 changes: 5 additions & 0 deletions packages/atlas/lib/src/atlas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ class Atlas extends StatelessWidget {
/// Callback executed when the map is loaded and is on the screen
final VoidCallback? onMapLoaded;

/// Callback executed when the map is loaded and is on the screen
final Heatmap? heatmap;

Atlas({
Key? key,
required this.initialCameraPosition,
Expand Down Expand Up @@ -175,6 +178,7 @@ class Atlas extends StatelessWidget {
this.maxZoom,
this.onPolylineTap,
this.onMapLoaded,
this.heatmap,
}) : markers = markers ?? Set<Marker>(),
callouts = callouts ?? Set<Callout>(),
circles = circles ?? Set<Circle>(),
Expand Down Expand Up @@ -224,6 +228,7 @@ class Atlas extends StatelessWidget {
maxZoom: maxZoom,
onPolylineTap: onPolylineTap,
onMapLoaded: onMapLoaded,
heatmap: heatmap,
);
}
}
24 changes: 24 additions & 0 deletions packages/atlas/lib/src/heatmap/heatcolor.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'dart:ui';

import 'heatcolor_interpolation.dart';

/// Defines the color of each pixel based on its density value in a heatmap
class Heatcolor {
final HeatcolorInterpolation interpolation;
final Map<double, Color> densityColors;

Heatcolor({required this.interpolation, required this.densityColors});

String toProperty() {
final density = densityColors.keys.map((density) {
final color = densityColors[density]!.toProperty();
return '$density,"$color"';
}).join(',');
return '["interpolate",${interpolation.toProperty()},["heatmap-density"],$density]';
}
}

extension ColorProperty on Color {
String toProperty() => 'rgba($red,$green,$blue,$alpha)';
}
27 changes: 27 additions & 0 deletions packages/atlas/lib/src/heatmap/heatcolor_interpolation.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/// Produces continuous, smooth results by interpolating between pairs of input and output value
abstract class HeatcolorInterpolation {
String toProperty();
}

class LinearInterpolation extends HeatcolorInterpolation {
@override
String toProperty() => '["linear"]';
}

class ExponentialInterpolation extends HeatcolorInterpolation {
final double base;
ExponentialInterpolation(this.base);
@override
String toProperty() => '["exponential", $base]';
}

class CubitInterpolation extends HeatcolorInterpolation {
final double x1;
final double y1;
final double x2;
final double y2;
CubitInterpolation(this.x1, this.y1, this.x2, this.y2);
@override
String toProperty() => '["cubic-bezier"], $x1, $y1, $x2, $y2]';
}
13 changes: 13 additions & 0 deletions packages/atlas/lib/src/heatmap/heatmap.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:atlas/src/heatmap/heatcolor.dart';

import 'heatpoint.dart';

/// Visualization to depict the intensity of data at geographical points.
class Heatmap {
final Set<Heatpoint> heatpoints;
final Heatcolor? heatcolor;
final bool showCounter;

const Heatmap({required this.heatpoints, this.heatcolor, this.showCounter = false});
}
11 changes: 11 additions & 0 deletions packages/atlas/lib/src/heatmap/heatpoint.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'package:atlas/src/lat_lng.dart';

/// The points where to build the heatmaps
class Heatpoint {
final String id;
final LatLng position;
final int counter;

const Heatpoint({required this.id, required this.position, required this.counter});
}
1 change: 1 addition & 0 deletions packages/atlas/lib/src/provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,6 @@ abstract class Provider {
final double? minZoom,
final double? maxZoom,
final VoidCallback? onMapLoaded,
final Heatmap? heatmap,
});
}
2 changes: 1 addition & 1 deletion packages/atlas/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: atlas
description: An extensible map abstraction for Flutter with support for multiple map providers
version: 1.0.21
version: 1.0.22
repository: https://github.com/bmw-tech/atlas
issue_tracker: https://github.com/bmw-tech/atlas/issues
homepage: https://bmw-tech.github.io/atlas
Expand Down
26 changes: 26 additions & 0 deletions packages/atlas/test/heatmap/heatcolor_interpolation_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'package:atlas/src/heatmap/heatcolor_interpolation.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('HeatcolorInterpolation Tests', () {
test('LinearInterpolation returns correct property', () {
final interpolation = LinearInterpolation();
expect(interpolation.toProperty(), '["linear"]');
});

test('ExponentialInterpolation returns correct property', () {
final base = 2.0;
final interpolation = ExponentialInterpolation(base);
expect(interpolation.toProperty(), '["exponential", $base]');
});

test('CubitInterpolation returns correct property', () {
final x1 = 0.42;
final y1 = 0.0;
final x2 = 0.58;
final y2 = 1.0;
final interpolation = CubitInterpolation(x1, y1, x2, y2);
expect(interpolation.toProperty(), '["cubic-bezier"], $x1, $y1, $x2, $y2]');
});
});
}
25 changes: 25 additions & 0 deletions packages/atlas/test/heatmap/heatcolor_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'dart:ui';

import 'package:atlas/src/heatmap/heatcolor.dart';
import 'package:atlas/src/heatmap/heatcolor_interpolation.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('Heatcolor Tests', () {
test('Heatcolor returns correct property string', () {
final interpolation = LinearInterpolation();
final densityColors = {
0.0: Color.fromARGB(255, 255, 0, 0), // Red
0.5: Color.fromARGB(255, 0, 255, 0), // Green
1.0: Color.fromARGB(255, 0, 0, 255), // Blue
};
final heatcolor = Heatcolor(interpolation: interpolation, densityColors: densityColors);
final expectedString = '["interpolate",["linear"],["heatmap-density"],'
'0.0,"rgba(255,0,0,255)",'
'0.5,"rgba(0,255,0,255)",'
'1.0,"rgba(0,0,255,255)"]';

expect(heatcolor.toProperty(), expectedString);
});
});
}
35 changes: 35 additions & 0 deletions packages/atlas/test/heatmap/heatmap_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'package:atlas/atlas.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('Heatmap Tests', () {
test('Heatmap is correctly instantiated with default values', () {
final heatpoints = <Heatpoint>{
Heatpoint(id: '1', position: LatLng(latitude: 37.7749, longitude: -122.4194), counter: 10),
Heatpoint(id: '2', position: LatLng(latitude: 34.0522, longitude: -118.2437), counter: 20),
};
final heatmap = Heatmap(heatpoints: heatpoints);

expect(heatmap.heatpoints, equals(heatpoints));
expect(heatmap.heatcolor, isNull);
expect(heatmap.showCounter, isFalse);
});

test('Heatmap correctly assigns non-default values', () {
final heatpoints = <Heatpoint>{
Heatpoint(id: '1', position: LatLng(latitude: 37.7749, longitude: -122.4194), counter: 10),
Heatpoint(id: '2', position: LatLng(latitude: 34.0522, longitude: -118.2437), counter: 20),
};
final heatcolor = Heatcolor(
interpolation: LinearInterpolation(),
densityColors: {0.0: Colors.red, 0.5: Colors.green, 1.0: Colors.blue},
);
final heatmap = Heatmap(heatpoints: heatpoints, heatcolor: heatcolor, showCounter: true);

expect(heatmap.heatpoints, equals(heatpoints));
expect(heatmap.heatcolor, equals(heatcolor));
expect(heatmap.showCounter, isTrue);
});
});
}
18 changes: 18 additions & 0 deletions packages/atlas/test/heatmap/heatpoint_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'package:atlas/atlas.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('Heatpoint Tests', () {
test('Heatpoint properties are correctly assigned', () {
final position = LatLng(latitude: 37.7749, longitude: -122.4194);
final id = 'testId';
final counter = 42;

final heatpoint = Heatpoint(id: id, position: position, counter: counter);

expect(heatpoint.id, id);
expect(heatpoint.position, position);
expect(heatpoint.counter, counter);
});
});
}

0 comments on commit 856344e

Please sign in to comment.