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

Mapkit won't center? #65

Open
khorwood-openai opened this issue Sep 6, 2024 · 9 comments
Open

Mapkit won't center? #65

khorwood-openai opened this issue Sep 6, 2024 · 9 comments

Comments

@khorwood-openai
Copy link

khorwood-openai commented Sep 6, 2024

Hey all, I'm evaluating using this project for a small demo.

I'm using this code, where marker and coords are identical objects.

<Map
    token={process.env.REACT_APP_MAPKIT_KEY || ""}
    initialRegion={{
        centerLatitude: coords.lat,
        centerLongitude: coords.lng,
        latitudeDelta: 0.2,
        longitudeDelta: 0.2
    }}
    cameraBoundary={{
        centerLatitude: coords.lat,
        centerLongitude: coords.lng,
        latitudeDelta: 0.2,
        longitudeDelta: 0.2
    }}
>
    {marker && <Marker
        latitude={marker.lat}
        longitude={marker.lng}
        title={marker.location}
    />}
</Map>

The Map is part of a flexbox component that looks roughly like;

._____________________.
| MAP HERE |          |
|----------|          |
|__________|__________|

Whenever I update coordinates with cameraBoundary, the map always centers on the bottom left of the boundary area.

Something I noticed when inspecting the HTML;

<div class="mk-map-view mk-disable-all-gestures" style="position: relative;">
  <canvas width="1122" height="494" class="syrup-canvas" aria-hidden="true" style="width: 561px; height: 247px; background-color: rgb(249, 245, 237);"></canvas>
  <canvas class="rt-root" lang="en-US" dir="ltr" aria-hidden="true" width="1122" height="494" style="width: 561px; height: 247px;"></canvas>
  <div class="mk-map-node-element" style="width: 561px; height: 247px;">
    <div class="mk-annotation-container" lang="en-US"></div>
    <div class="mk-controls-container" lang="en-US" style="inset: 0px;"></div>
  </div>
</div>

You'll notice the canvas width and height values are double the style values. My window.devicePixelRatio setting is set to 2, and I think this may be related to the cause.

Any chance y'all can fix this? I'm happy to just use the default MapKit library but this seemed like a neat and simple solution.

@khorwood-openai
Copy link
Author

Just to be clear; sometimes the marker isn't even inside of the visible map range when I update coords and marker. I have verified that they are identical objects / have the same values.

@Nicolapps
Copy link
Owner

Hi @khorwood-openai,

Thank you for your interest in mapkit-react!

The strange behavior you are facing seems to come from MapKit JS itself. The cameraBoundary property available in mapkit-react maps directly to cameraBoundary in MapKit JS. It only controls the range the user is allowed to pan the camera in, not the actual position of the camera. When setting it after map initialization, it seems that MapKit JS will try to move the camera to the closest location where it would fit the new camera boundary, which is not what you want.

What I would suggest doing instead is to use MapKit JS’s imperative API (which is available when using mapkit-react through ref) to move the camera whenever you need:

import React, { useRef, useState } from 'react';
import { Map, Marker } from 'mapkit-react';

const token = "…";

export const MapWithMovingMarker = () => {
  const locations = [
    { name: 'Jet d’eau', latitude: 46.20738751546706, longitude: 6.155891756231 },
    { name: 'Horloge fleurie', latitude: 46.20418989653241, longitude: 6.150997214058191 },
    { name: 'Bains des Pâquis', latitude: 46.21042332922134, longitude: 6.154311850592231 },
  ];

  const [selectedLocationIndex, setSelectedLocationIndex] = useState(0);
  const selectedLocation = locations[selectedLocationIndex];

  const mapRef = useRef<mapkit.Map | null>(null);

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      <div style={{ flex: 1 }}>
        <Map
          initialRegion={{
            centerLatitude: selectedLocation.latitude,
            centerLongitude: selectedLocation.longitude,
            latitudeDelta: 0.005,
            longitudeDelta: 0.005,
          }}
          token={token}
          ref={mapRef}
        >
          <Marker
            latitude={selectedLocation.latitude}
            longitude={selectedLocation.longitude}
            title={selectedLocation.name}
          />
        </Map>
      </div>
      <div style={{ padding: '1rem' }}>
        <select
          value={selectedLocationIndex}
          onChange={(e) => {
            const newIndex = Number(e.target.value);
            setSelectedLocationIndex(newIndex);

            mapRef.current?.setCenterAnimated(new mapkit.Coordinate(
              locations[newIndex].latitude,
              locations[newIndex].longitude,
            ));
          }}
        >
          {locations.map((location, index) => (
            <option key={location.name} value={index}>{location.name}</option>
          ))}
        </select>
      </div>
    </div>
  );
};
Screenshot.2024-09-08.at.12.03.36.mov

@passatgt
Copy link

passatgt commented Sep 9, 2024

How can you center and zoom in?

@Nicolapps
Copy link
Owner

@passatgt You can use setRegionAnimated or setCameraDistanceAnimated.

@passatgt
Copy link

Thanks, thats what i figured. Its a bit difficoult to use setRegionAnimated, because you don't actually set zoom level, but the visible area diameter. And doing setCenterAnimated with setCameraDistanceAnimated doesn't seem to work if you run them both at the same time. Kinda weird we don't have a simple zoom levels like Google Maps from 0 to 22 or something like that.

@parttiez
Copy link

Thanks, thats what i figured. Its a bit difficoult to use setRegionAnimated, because you don't actually set zoom level, but the visible area diameter. And doing setCenterAnimated with setCameraDistanceAnimated doesn't seem to work if you run them both at the same time. Kinda weird we don't have a simple zoom levels like Google Maps from 0 to 22 or something like that.

yeah... I am having a hard time adjusting to the map too. Also I don't know if it is just me but the document from Apple seems really hard to read and know what the hack I have to do with. I don't see any example or anything like that to have a clue. Like glyphImage. It says 'Object' but what object haha

@passatgt
Copy link

Thanks, thats what i figured. Its a bit difficoult to use setRegionAnimated, because you don't actually set zoom level, but the visible area diameter. And doing setCenterAnimated with setCameraDistanceAnimated doesn't seem to work if you run them both at the same time. Kinda weird we don't have a simple zoom levels like Google Maps from 0 to 22 or something like that.

yeah... I am having a hard time adjusting to the map too. Also I don't know if it is just me but the document from Apple seems really hard to read and know what the hack I have to do with. I don't see any example or anything like that to have a clue. Like glyphImage. It says 'Object' but what object haha

This is my project, looks and kinda works nice: https://points.kvikk.hu
But definitely a steep learning curve, but going to be a lot cheaper compared to Google Maps.

@parttiez
Copy link

Hi @khorwood-openai,

Thank you for your interest in mapkit-react!

The strange behavior you are facing seems to come from MapKit JS itself. The cameraBoundary property available in mapkit-react maps directly to cameraBoundary in MapKit JS. It only controls the range the user is allowed to pan the camera in, not the actual position of the camera. When setting it after map initialization, it seems that MapKit JS will try to move the camera to the closest location where it would fit the new camera boundary, which is not what you want.

What I would suggest doing instead is to use MapKit JS’s imperative API (which is available when using mapkit-react through ref) to move the camera whenever you need:

import React, { useRef, useState } from 'react';
import { Map, Marker } from 'mapkit-react';

const token = "…";

export const MapWithMovingMarker = () => {
  const locations = [
    { name: 'Jet d’eau', latitude: 46.20738751546706, longitude: 6.155891756231 },
    { name: 'Horloge fleurie', latitude: 46.20418989653241, longitude: 6.150997214058191 },
    { name: 'Bains des Pâquis', latitude: 46.21042332922134, longitude: 6.154311850592231 },
  ];

  const [selectedLocationIndex, setSelectedLocationIndex] = useState(0);
  const selectedLocation = locations[selectedLocationIndex];

  const mapRef = useRef<mapkit.Map | null>(null);

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      <div style={{ flex: 1 }}>
        <Map
          initialRegion={{
            centerLatitude: selectedLocation.latitude,
            centerLongitude: selectedLocation.longitude,
            latitudeDelta: 0.005,
            longitudeDelta: 0.005,
          }}
          token={token}
          ref={mapRef}
        >
          <Marker
            latitude={selectedLocation.latitude}
            longitude={selectedLocation.longitude}
            title={selectedLocation.name}
          />
        </Map>
      </div>
      <div style={{ padding: '1rem' }}>
        <select
          value={selectedLocationIndex}
          onChange={(e) => {
            const newIndex = Number(e.target.value);
            setSelectedLocationIndex(newIndex);

            mapRef.current?.setCenterAnimated(new mapkit.Coordinate(
              locations[newIndex].latitude,
              locations[newIndex].longitude,
            ));
          }}
        >
          {locations.map((location, index) => (
            <option key={location.name} value={index}>{location.name}</option>
          ))}
        </select>
      </div>
    </div>
  );
};

Screenshot.2024-09-08.at.12.03.36.mov

Heya! In order to do this, do I have to add

<script src="https://cdn.apple-mapkit.com/mk/5.x.x/mapkit.core.js" crossorigin async data-callback="initMapKit" data-libraries="services,full-map,geojson"></script>
to html? BTW, thanks you so much for the great package :)

@Nicolapps
Copy link
Owner

Hey @parttiez, thank you for your interest in mapkit-react!

You don’t need to add <script> tags when using mapkit-react, it will automatically load the necessary script from Apple’s CDN. If you would like to change the way the script is loaded (for instance because you only want to load only some parts of MapKit JS instead of the entire bundle to improve your app’s performance), you can use the load parameter of <Map />.

Don’t forget to include a correct value for token (you can learn how to generate one from Apple’s documentation).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants