flutter_map icon indicating copy to clipboard operation
flutter_map copied to clipboard

fitBounds does not work as expected when a Map is rotated

Open SheenaJacob opened this issue 3 years ago • 3 comments

What is the bug?

The aim of this example is to be able to fit the map to contain the four specified bounds. When the Fit Bounds button is clicked with no Map Rotation then it works as expected, but when a Map is first rotated and then the FitBounds button is pressed this is no longer the case.

What is the expected behaviour?

When a map is not rotated fitBounds works as expected

expected

When a map is rotated then the map no longer shows all the bounds. After_rotating

How can we reproduce this issue?

import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';

void main() {
  runApp(FitBounds());
}

class FitBounds extends StatelessWidget {
  FitBounds({Key? key}) : super(key: key);

  static LatLng topRightCorner = LatLng(4.214943, 33.925781);
  static LatLng topLeftCorner = LatLng(3.480523, 30.844116);
  static LatLng bottomLeftCorner = LatLng(-1.362176, 29.575195);
  static LatLng bottomRightCorner = LatLng(-0.999705, 33.925781);
  final MapController mapController = MapController();

  @override
  Widget build(BuildContext context) {
    final markers = <Marker>[
      Marker(
        width: 100,
        height: 100,
        point: topLeftCorner,
        builder: (ctx) => const Icon(Icons.circle, color: Colors.green),
      ),
      Marker(
        width: 100,
        height: 100,
        point: topRightCorner,
        builder: (ctx) => const Icon(Icons.circle, color: Colors.blue),
      ),
      Marker(
        width: 100,
        height: 100,
        point: bottomLeftCorner,
        builder: (ctx) => const Icon(Icons.circle, color: Colors.red),
      ),
      Marker(
        width: 100,
        height: 100,
        point: bottomRightCorner,
        builder: (ctx) => const Icon(Icons.circle, color: Colors.purple),
      ),
    ];

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Animated MapController')),
        body: Padding(
          padding: const EdgeInsets.all(8),
          child: Column(
            children: [
              Padding(
                padding: const EdgeInsets.only(top: 8, bottom: 8),
                child: Row(
                  children: <Widget>[
                    MaterialButton(
                      onPressed: () {
                        final bounds = LatLngBounds.fromPoints([
                          topLeftCorner,
                          topRightCorner,
                          bottomLeftCorner,
                          bottomRightCorner
                        ]);
                        mapController.fitBounds(
                          bounds,
                          options: const FitBoundsOptions(
                            padding: EdgeInsets.all(20),
                          ),
                        );
                      },
                      child: const Text('Fit Bounds'),
                    ),
                  ],
                ),
              ),
              Flexible(
                child: FlutterMap(
                  mapController: mapController,
                  options: MapOptions(
                      center: LatLng(2.108899, 33.112793),
                      zoom: 5,
                      maxZoom: 10,
                      minZoom: 3),
                  layers: [
                    TileLayerOptions(
                      urlTemplate:
                      'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
                      subdomains: ['a', 'b', 'c'],
                      userAgentPackageName: 'dev.fleaflet.flutter_map.example',
                    ),
                    MarkerLayerOptions(markers: markers),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Do you have a potential solution?

While I don't have a solution I assume that there is a problem when calculating the new zoom value, as the problem occurs while using two of the methods in the MapController class.

  1. centerZoomFitBounds(LatLngBounds bounds, {FitBoundsOptions? options})
  2. fitBounds(LatLngBounds bounds, {FitBoundsOptions? options}).

In the MapState class (https://github.com/fleaflet/flutter_map/blob/master/lib/src/map/map.dart) both these methods use the getBoundsCenterZoom(LatLngBounds bounds, FitBoundsOptions options) function where the new center and zoom value are calculated. While the center is calculated correctly, the zoom (calculated by getBoundsZoom(LatLngBounds bounds, CustomPoint padding,{bool inside = false}) )value does not take into account that a map is rotated.

Can you provide any other information?

No response

Platforms Affected

Android

Severity

Minimum: Allows normal functioning

Frequency

Consistently: Always occurs at the same time and location

Requirements

  • [X] I agree to follow this project's Code of Conduct
  • [X] My Flutter/Dart installation is unaltered, and flutter doctor finds no relevant issues
  • [X] I am using the latest stable version of this package
  • [X] I have checked the FAQs section on the documentation website
  • [X] I have checked for similar issues which may be duplicates

SheenaJacob avatar Aug 16 '22 10:08 SheenaJacob

I haven't tested this yet, but think this has been raised before and don't think there's any code to deal with it yet, so I think makes sense it wouldn't work.

I think this will get done at some point, but pull requests welcome, to make it sooner!

ibrierley avatar Aug 17 '22 22:08 ibrierley

Try to set rotation to zero before fitbounds

mziad-dev avatar Aug 25 '22 05:08 mziad-dev

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar Sep 25 '22 02:09 github-actions[bot]

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar Oct 26 '22 02:10 github-actions[bot]

I think have fixed this in my fork, still testing. My code looks slightly different, but it's a quick fix anyway.

In getBoundsZoom:

  1. Use nonrotatedSize, the size of the viewport itself (which is our reference for fitting) is not affected by rotation.
  2. Instead, rotate the bounding box we are fitting the viewport to, just as we do with the viewport in _updateSizeByOriginalSizeAndRotation.

EDIT: The rotated bounding box approach works well when the layers fill the whole non-rotated bounds, but with vector layers for example, it feels a bit less intuitive to the untrained eye since the bounding box itself is not visible...

The bounding box may have some transparent regions which this approach takes care to include, making a rotated fit a bit "loose"/too zoomed out. Maybe there is a smarter way, this was just the most straight-forward solution that came up from the top of my head...

JosefWN avatar Nov 01 '22 06:11 JosefWN

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar Dec 02 '22 02:12 github-actions[bot]

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar Jan 02 '23 01:01 github-actions[bot]