Update and match marker style betwen google and osm backend (as much as possible)

This commit is contained in:
Ming Ming 2024-07-23 01:11:34 +08:00
parent 3c94741da3
commit a7ef3b0ed7
4 changed files with 152 additions and 99 deletions

View file

@ -23,6 +23,7 @@ import 'package:nc_photos/exception_event.dart';
import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/snack_bar_manager.dart';
import 'package:nc_photos/stream_extension.dart';
import 'package:nc_photos/stream_util.dart';
import 'package:nc_photos/theme.dart';
import 'package:nc_photos/theme/dimension.dart';
import 'package:nc_photos/widget/collection_browser.dart';

View file

@ -14,16 +14,8 @@ class _DataPoint extends DataPoint {
final int fileId;
}
class _GoogleMarkerBuilder {
_GoogleMarkerBuilder(this.context);
Future<BitmapDescriptor> build(List<DataPoint> dataPoints) {
return _getClusterBitmap(
_getMarkerSize(dataPoints.length),
text: _getMarkerCountString(dataPoints.length),
color: _getMarkerColor(dataPoints.length),
);
}
class _MarkerBuilder {
_MarkerBuilder(this.context);
String _getMarkerCountString(int count) {
switch (count) {
@ -40,7 +32,7 @@ class _GoogleMarkerBuilder {
}
}
Color _getMarkerColor(int count) {
double _getMarkerRatio(int count) {
const step = 1 / 4;
final double r;
switch (count) {
@ -55,43 +47,77 @@ class _GoogleMarkerBuilder {
default:
r = (count / 10) * step;
}
if (Theme.of(context).brightness == Brightness.light) {
return HSLColor.fromAHSL(
1,
_colorHsl.hue,
r * .7 + .3,
(_colorHsl.lightness - (.1 - r * .1)).clamp(0, 1),
).toColor();
} else {
return HSLColor.fromAHSL(
1,
_colorHsl.hue,
r * .6 + .4,
(_colorHsl.lightness - (.1 - r * .1)).clamp(0, 1),
).toColor();
}
return r;
}
int _getMarkerSize(int count) {
const step = 1 / 4;
final double r;
switch (count) {
case >= 10000:
r = 1;
case >= 1000:
r = (count ~/ 1000) / 10 * step + step * 3;
case >= 100:
r = (count ~/ 100) / 10 * step + step * 2;
case >= 10:
r = (count ~/ 10) / 10 * step + step;
default:
r = (count / 10) * step;
}
return (r * 85).toInt() + 85;
final BuildContext context;
late final _minColorHsl =
HSLColor.fromColor(Theme.of(context).colorScheme.primary)
.withSaturation(
Theme.of(context).brightness == Brightness.light ? .9 : .7)
.withLightness(
Theme.of(context).brightness == Brightness.light ? .4 : .3);
late final _maxColorHsl =
HSLColor.fromColor(Theme.of(context).colorScheme.primary)
.withSaturation(
Theme.of(context).brightness == Brightness.light ? .9 : .7)
.withLightness(
Theme.of(context).brightness == Brightness.light ? .3 : .2);
}
class _OsmMarkerBuilder extends _MarkerBuilder {
_OsmMarkerBuilder(super.context);
Widget build(List<DataPoint> dataPoints) {
final text = _getMarkerCountString(dataPoints.length);
return _OsmMarker(
size: _getMarkerSize(dataPoints.length),
text: text,
textSize: _getMarkerTextSize(text, dataPoints.length),
color: _getMarkerColor(dataPoints.length),
);
}
double _getMarkerSize(int count) {
final r = _getMarkerRatio(count);
return (r * 28).toInt() + 28;
}
double _getMarkerTextSize(String text, int count) {
final r = _getMarkerRatio(count);
return (r * 3) + 9 - ((text.length / 6) * 1);
}
Color _getMarkerColor(int count) {
final r = _getMarkerRatio(count);
return HSLColor.lerp(_minColorHsl, _maxColorHsl, r)!.toColor();
}
}
class _GoogleMarkerBuilder extends _MarkerBuilder {
_GoogleMarkerBuilder(super.context);
Future<BitmapDescriptor> build(List<DataPoint> dataPoints) {
return _getClusterBitmap(
_getMarkerSize(dataPoints.length),
text: _getMarkerCountString(dataPoints.length),
color: _getMarkerColor(dataPoints.length),
);
}
double _getMarkerSize(int count) {
final r = _getMarkerRatio(count);
return (r * 75).toInt() + 100;
}
Color _getMarkerColor(int count) {
final r = _getMarkerRatio(count);
return HSLColor.lerp(_minColorHsl, _maxColorHsl, r)!.toColor();
}
Future<BitmapDescriptor> _getClusterBitmap(
int size, {
double size, {
String? text,
required Color color,
}) async {
@ -99,9 +125,7 @@ class _GoogleMarkerBuilder {
final Canvas canvas = Canvas(pictureRecorder);
final fillPaint = Paint()..color = color;
final outlinePaint = Paint()
..color = Theme.of(context).brightness == Brightness.light
? Colors.black.withOpacity(.28)
: Colors.white.withOpacity(.6)
..color = Colors.white.withOpacity(.75)
..strokeWidth = size / 28
..style = PaintingStyle.stroke;
const shadowPadding = 6.0;
@ -126,7 +150,7 @@ class _GoogleMarkerBuilder {
text: text,
style: TextStyle(
fontSize: size / 3 - ((text.length / 6) * (size * 0.1)),
color: Theme.of(context).colorScheme.onPrimaryContainer,
color: Colors.white.withOpacity(.75),
fontWeight: FontWeight.normal,
),
);
@ -139,15 +163,11 @@ class _GoogleMarkerBuilder {
),
);
}
final img = await pictureRecorder.endRecording().toImage(size, size);
final img =
await pictureRecorder.endRecording().toImage(size.ceil(), size.ceil());
final data = await img.toByteData(format: ImageByteFormat.png) as ByteData;
return BitmapDescriptor.fromBytes(data.buffer.asUint8List());
}
final BuildContext context;
late final _colorHsl =
HSLColor.fromColor(Theme.of(context).colorScheme.primaryContainer);
}
enum _DateRangeType {

View file

@ -26,42 +26,44 @@ class _MapViewState extends State<_MapView> {
builder: (context, state) {
final prevPosition =
context.read<PrefController>().mapBrowserPrevPositionValue;
return InteractiveMap(
providerHint: GpsMapProvider.google,
initialPosition: prevPosition ?? const MapCoord(0, 0),
initialZoom: prevPosition == null ? 2.5 : 10,
dataPoints: state.data,
onClusterTap: (dataPoints) {
final c = Collection(
name: "",
contentProvider: CollectionAdHocProvider(
account: context.bloc.account,
fileIds: dataPoints
.cast<_DataPoint>()
.map((e) => e.fileId)
.toList(),
),
);
Navigator.of(context).pushNamed(
CollectionBrowser.routeName,
arguments: CollectionBrowserArguments(c),
);
},
googleClusterBuilder: (context, dataPoints) =>
_GoogleMarkerBuilder(context).build(dataPoints),
osmClusterBuilder: (context, dataPoints) => _OsmMarker(
count: dataPoints.length,
return ValueStreamBuilder<GpsMapProvider>(
stream: context.bloc.prefController.gpsMapProvider,
builder: (context, gpsMapProvider) => InteractiveMap(
providerHint: gpsMapProvider.requireData,
initialPosition: prevPosition ?? const MapCoord(0, 0),
initialZoom: prevPosition == null ? 2.5 : 10,
dataPoints: state.data,
onClusterTap: (dataPoints) {
final c = Collection(
name: "",
contentProvider: CollectionAdHocProvider(
account: context.bloc.account,
fileIds: dataPoints
.cast<_DataPoint>()
.map((e) => e.fileId)
.toList(),
),
);
Navigator.of(context).pushNamed(
CollectionBrowser.routeName,
arguments: CollectionBrowserArguments(c),
);
},
googleClusterBuilder: (context, dataPoints) =>
_GoogleMarkerBuilder(context).build(dataPoints),
osmClusterBuilder: (context, dataPoints) =>
_OsmMarkerBuilder(context).build(dataPoints),
contentPadding: EdgeInsets.only(
top: MediaQuery.of(context).padding.top,
bottom: MediaQuery.of(context).padding.bottom,
),
onMapCreated: (controller) {
_controller = controller;
if (state.initialPoint != null) {
controller.setPosition(state.initialPoint!);
}
},
),
contentPadding: EdgeInsets.only(
top: MediaQuery.of(context).padding.top,
bottom: MediaQuery.of(context).padding.bottom,
),
onMapCreated: (controller) {
_controller = controller;
if (state.initialPoint != null) {
controller.setPosition(state.initialPoint!);
}
},
);
},
),
@ -73,26 +75,50 @@ class _MapViewState extends State<_MapView> {
class _OsmMarker extends StatelessWidget {
const _OsmMarker({
required this.count,
required this.size,
required this.text,
required this.textSize,
required this.color,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Theme.of(context).colorScheme.primary,
),
child: Center(
child: Text(
count.toString(),
style: const TextStyle(color: Colors.white),
return Center(
child: Container(
width: size,
height: size,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(size / 2),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(.3),
blurRadius: 2,
offset: const Offset(1, 1),
),
],
border: Border.all(
color: Colors.white.withOpacity(.75),
width: 1.5,
),
color: color,
),
child: Center(
child: Text(
text,
style: TextStyle(
fontSize: textSize,
color: Colors.white.withOpacity(.75),
),
),
),
),
);
}
final int count;
final double size;
final String text;
final double textSize;
final Color color;
}
class _PanelContainer extends StatefulWidget {

View file

@ -73,6 +73,12 @@ class _OsmInteractiveMapState extends State<OsmInteractiveMap> {
context,
markers.cast<_OsmDataPoint>().map((e) => e.original).toList(),
),
// need to be large enough to contain markers of all size
size: const Size.square(120),
// disable all tap handlers from package
zoomToBoundsOnClick: false,
centerMarkerOnClick: false,
spiderfyCluster: false,
),
),
Padding(