Skip to content

Marker dimensions using meters #2106

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

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
46 changes: 44 additions & 2 deletions example/lib/pages/markers.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_example/misc/tile_providers.dart';
Expand Down Expand Up @@ -117,8 +119,11 @@ class _MarkerPageState extends State<MarkerPage> {
Flexible(
child: FlutterMap(
options: MapOptions(
initialCenter: const LatLng(51.5, -0.09),
initialZoom: 5,
initialCenter: const LatLng(
51.51868093513547,
-0.12835376940892318,
),
initialZoom: 15,
onTap: (_, p) => setState(() => customMarkers.add(buildPin(p))),
interactionOptions: const InteractionOptions(
flags: ~InteractiveFlag.doubleTapZoom,
Expand Down Expand Up @@ -167,6 +172,43 @@ class _MarkerPageState extends State<MarkerPage> {
rotate: counterRotate,
alignment: selectedAlignment,
),
MarkerLayer(
markers: [
Marker(
point: const LatLng(
51.51868093513547,
-0.12835376940892318,
),
height: 20,
width: 20,
boxConstraintsUsingMetersInPixels: const BoxConstraints(
maxHeight: 200,
maxWidth: 200,
),
child: LayoutBuilder(
builder: (context, constraints) {
final minDimension = min(
constraints.maxHeight,
constraints.maxWidth,
);

return Transform.scale(
scale: minDimension / 30,
child: const SizedBox(
width: 30,
height: 30,
child: Icon(
Icons.map,
color: Colors.amber,
),
),
);
},
),
useSizeInMeters: true,
),
],
),
],
),
),
Expand Down
13 changes: 13 additions & 0 deletions lib/src/layer/marker_layer/marker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ class Marker {
/// marker. Use a widget inside [child] to perform this.
final bool? rotate;

/// Parameter to enable or not the feature to use markers dimensions in meters.
///
/// A good way to use that feature is using a LayoutBuilder and building according the
/// maxHeight and minWidth values.
final bool useSizeInMeters;

/// Parameter to control the box size when the parameter [useSizeInMeters] is enabled.
/// That BoxConstraints is optional and when exists control the minimal and maximal size
/// in pixels of that region created by the map visible region in meters.
final BoxConstraints? boxConstraintsUsingMetersInPixels;

/// Creates a container for a [child] widget located at a geographic coordinate
/// [point]
///
Expand All @@ -61,6 +72,8 @@ class Marker {
this.height = 30,
this.alignment,
this.rotate,
this.useSizeInMeters = false,
this.boxConstraintsUsingMetersInPixels,
});

/// Returns the alignment of a [width]x[height] rectangle by [left]x[top] pixels.
Expand Down
52 changes: 43 additions & 9 deletions lib/src/layer/marker_layer/marker_layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,53 @@ class MarkerLayer extends StatelessWidget {
child: Stack(
children: (List<Marker> markers) sync* {
for (final m in markers) {
// Resolve real alignment
// TODO: maybe just using Size, Offset, and Rect?
final left = 0.5 * m.width * ((m.alignment ?? alignment).x + 1);
final top = 0.5 * m.height * ((m.alignment ?? alignment).y + 1);
final right = m.width - left;
final bottom = m.height - top;

// Perform projection
final pxPoint = map.projectAtZoom(m.point);

Positioned? getPositioned(double worldShift) {
final shiftedX = pxPoint.dx + worldShift;

double height = m.height;
double width = m.width;

if (m.useSizeInMeters) {
final basePoint = m.point;
final baseOffset = map.getOffsetFromOrigin(basePoint);
final rHeight =
const Distance().offset(basePoint, height / 2, 0);
final rWidth = const Distance().offset(basePoint, width / 2, 0);

height =
(baseOffset - map.getOffsetFromOrigin(rHeight)).distance *
2;
width =
(baseOffset - map.getOffsetFromOrigin(rWidth)).distance * 2;

final boxConstraintsUsingMetersInPixels =
m.boxConstraintsUsingMetersInPixels;
if (boxConstraintsUsingMetersInPixels != null) {
if (height > boxConstraintsUsingMetersInPixels.maxHeight) {
height = boxConstraintsUsingMetersInPixels.maxHeight;
}
if (width > boxConstraintsUsingMetersInPixels.maxWidth) {
width = boxConstraintsUsingMetersInPixels.maxWidth;
}
if (height < boxConstraintsUsingMetersInPixels.minHeight) {
height = boxConstraintsUsingMetersInPixels.minHeight;
}
if (width < boxConstraintsUsingMetersInPixels.minWidth) {
width = boxConstraintsUsingMetersInPixels.minWidth;
}
}
}

// Resolve real alignment
// TODO: maybe just using Size, Offset, and Rect?
final left = 0.5 * width * ((m.alignment ?? alignment).x + 1);
final top = 0.5 * height * ((m.alignment ?? alignment).y + 1);
final right = width - left;
final bottom = height - top;

// Cull if out of bounds
if (!map.pixelBounds.overlaps(
Rect.fromPoints(
Expand All @@ -77,8 +111,8 @@ class MarkerLayer extends StatelessWidget {

return Positioned(
key: m.key,
width: m.width,
height: m.height,
width: width,
height: height,
left: shiftedLocalPoint.dx - right,
top: shiftedLocalPoint.dy - bottom,
child: (m.rotate ?? rotate)
Expand Down