mapbox-unity-sdk
mapbox-unity-sdk copied to clipboard
Drawing range circle based on latitude longitude and radius in meters
Hi everyone,
As the title says I'm trying to draw a circular range around a specific latlong coordinate
I've been using Mapbox for quite a while in some of my Unity applications, I've done some digging around and I'm almost there but it still seems to be a bit off. I know this could easily become a really though problem involving mercator math but I was hoping I could use the inner workings of Mapbox to get this one done as there's no feature support for this.
This is my code this far:
public class RadiusScaler : MonoBehaviour
{
[SerializeField] private float radiusMeters = 10f;
[SerializeField] private Vector2d center;
[SerializeField] AbstractMap _map;
public void SetRadius(float radiusMeters, Vector2d center)
{
this.radiusMeters = radiusMeters;
this.center = center;
}
private void Update()
{
//Debug.Log("Setting playerRadius from " + PlayerRadius.transform.localScale.x + " scale by " + (1/scaleFactor));
RectD referenceTileRect = Conversions.TileBounds(TileCover.CoordinateToTileId(center, _map.AbsoluteZoom));
//There's something off with this equation but seems to be a constant value.
double unitsPerMeter = _map.Options.scalingOptions.unityTileSize / referenceTileRect.Size.x;
double localScale = Mathd.Pow(2d, _map.Zoom - _map.AbsoluteZoom);
transform.localScale = Vector3.one * (float) (radiusMeters * 2d * unitsPerMeter * localScale);
}
}
First I get the tile that contains my coordinate with the current zoom level, then I calculate the size of a tile in units over the size in meters and to my understanding I'd get units/meter (this chunk is copying the MapScalingAtUnityScaleStrategy which I'm using). Next is the current local scale being applied according to the zoom level, as the map needs to be scaled between different tilesets for different absolute zoom levels (integer) (Grabbed it from the AbstractMap class). Then I apply that multiplied by twice the radius to get the size of my circle (the circle sprite is 1unit without any scale applied).
I got it to scale according to the map zoom and to remain consistent as you change the zoom values, however it seems to be off by a little bit comparing to other map building tools.
I'm using the AbstractMap with a zoom mechanism based on the Zoom Map example included with the SDK.
Anything I missed out like an internal constant or something that I need to account for? I tried adding a multiplier until the size matches what I expect but a constant value doesn't work for different radius sizes and it doesn't seem to be a linear relation.
Note: If this is a bug or support ticket, please provide the following information:
- Unity version: 2019.2.17f1
- Scripting Runtime Version:
- Scripting Backend: IL2CPP
- Api Compatibility Level: .NET Standard 2.0
- Mapbox SDK version: 2.0.0, as of October 15th, 2018
- The platform you're building to: iOS, Android
- A description of what you're trying to do: Explained on top^
- Steps to recreate the bug if appropriate: Create a script containing the code above, set any latlong and radius, compare the size with other mapbuilding tools like https://www.freemaptools.com/radius-around-point.htm
- Links to your logs are also helpful:
@halzate93 thank you very much for detailed ticket!
Distance conversions can be very confusing. Before trying it out myself though, my first hunch was tile gameobject scales. You mentioned you're using something based on zoomable map demo which scales map and tiles to compensate for camera zoom (i.e. from z15 to z15.5). I think it's not included into world relative scale
property.
So what I would suggest would be multiplying (or dividing?) that distance value by the lossy scale of a tile, any tile. Getting tile might be tricky but we have helper functions inside AbstractMap
and AbstractMap.MapVisualizer
.
Hope that helps!
OK so hear me out. I tried doing the same, to make the basic google maps style blue circle around the player's location, to show accuracy of gps tracking. Therefore I need to know how much is 1 meter on the map, in unity units, so I can scale objects by that. I could not find an API for it, so I made a hack.
public static float GetOneMapMeterInUnityMeters(AbstractMap map, Vector3 targetPlayerPos)
{
// hackish way of doing the conversion, since there is no obvious way of doing it in the API
// taking a position X meters away, converting to map coords, and then back into unity world coords.
var playerPosGps = map.WorldToGeoPosition(targetPlayerPos);
var playerPosInMeters = Conversions.LatLonToMeters(playerPosGps);
var playerPosInMeters1MeterAway = playerPosInMeters + new Vector2d(1, 0);
var oneMeterAwayLatLon = Conversions.MetersToLatLon(playerPosInMeters1MeterAway);
var worldPosOneMeterAway = map.GeoToWorldPosition(oneMeterAwayLatLon);
var oneMapMeterInUnityMeters = (worldPosOneMeterAway - targetPlayerPos).magnitude;
return oneMapMeterInUnityMeters;
}
Then I used this function to multiply the scale of the object I want to have X meters on the map, assuming its scale is already in meters=unity units. Seems to work, I tested roughly by comparing to other map software... But it feels super hackish and makes a LOT of calculations compared to a smarter method.....
Hope this helps!
@horatiu665 Thx very much providing your solution. I tumbled over the same situation and found out that no matter which zoom level i'm using i need to apply the same multiplicator on my scaling. Your calculation to get this multiplicator saved me time and nerves :-)
But the question is why this is not included into the WorldRelativeScale Method of AbstractMap ?
Hello! I am trying to implement @horatiu665's solution for this and am having some issues. Did you add this into AbstractMap? How did you then access it in the rest of the code to implement the circle? Thanks in advance for any help you can provide.
@devNMY could you possibly shine light on this?
@Anaph0ra you can put my function anywhere in some script you made, as long as you pass it a reference to AbstractMap when you use it. I don't remember how to get a reference to AbstractMap, but I think it is a component in the scene, so you can drag n drop it to a public field in your own component.
@DevNMY you're welcome! :)