Hi Antti, I'll provide some examples below. There's more. I've written more than a thousand lines of workarounds last week and it is hard to keep track of it all! Another strange behavior I uncovered was the map view extent going null when the map center is set to NaN. Granted, NaN is nonesense, but SetViewAsync should complain/throw right away instead. The NaN made it to the map view because of innocent looking code like this:
static async void TestExtentAndSetView(MapView map, MapPoint point)
{
var p = Project(NormalizeCentralMeridian(point), map.SpatialReference);
var extent = NormalizeCentralMeridian(map.Extent);
if (!Within(p, extent))
{
try { await map.SetViewAsync(point, TimeSpan.FromSeconds(1.5)); }
catch (Exception ex) { /*log the error*/ }
}
}
I have since switched over to my own "envelope" and "projection" code that is easier to use and get right.
GeometryEngine.Project
static MapPoint EsriProject(double lat, double lon)
{
return (MapPoint)GeometryEngine.Project(new MapPoint(lon, lat, SpatialReferences.Wgs84), SpatialReferences.WebMercator);
}
static MapPoint EsriUnProject(double x, double y)
{
return (MapPoint)GeometryEngine.Project(new MapPoint(x, y, SpatialReferences.WebMercator), SpatialReferences.Wgs84);
}
public void Test()
{
Action<double> xNaN = lon => Assert.IsTrue(double.IsNaN(EsriProject(0, lon).X));
xNaN(-180);
xNaN(180);
xNaN(-179.99999);
xNaN(179.99999);
}
const double Wgs84EarthSemiMajorRadiusMeters = 6378137;
const double webMercatorMaxX = Wgs84EarthSemiMajorRadiusMeters * System.Math.PI;
var p1 = EsriUnProject(webMercatorMaxX, 0);
var p2 = EsriUnProject(-webMercatorMaxX, 0);
System.Console.WriteLine(p1); // prints MapPoint[X=NaN, Y=NaN, Wkid=4326]
System.Console.WriteLine(p2); // prints MapPoint[X=NaN, Y=NaN, Wkid=4326]
Envelopes, Polyline, etc
Envelope unions do simple min/max and do not handle the wrap around. That means that the a union operation may connect the long way across the world when the intent is the short way. I've written my own "GeoRect" code that has a well defined left and right and can handle the wraparound. In other places, I do something like this:
static void FixXsForEsri(ref double x1, ref double x2, double y1, double y2, double expectedLength)
{
var distSqd = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
bool simpleConnectWouldBeTheLongWayAcrossTheWorld = distSqd > expectedLength * expectedLength * 2.0;
if (simpleConnectWouldBeTheLongWayAcrossTheWorld)
{
// Map X on negative side of the projection origin to the right.
if (x2 < 0.0)
x2 += GeoRectMaxBounds.WebMercator.Width;
else if (x1 < 0.0)
x1 += GeoRectMaxBounds.WebMercator.Width;
}
}
I can see how the way Envelope behaves could be more renderer friendly. Simple min/max-ing is less "branchy" than the wrap test code. From a map API user's view, it is quite unfriendly.
Simple Repro:
var p1 = new MapPoint(179, 0, SpatialReferences.Wgs84);
var p2 = GeometryEngine.GeodesicMove(p1, 1e3, LinearUnits.Kilometers, 90);
var e1 = new Envelope(p1, p1);
var e2 = new Envelope(p2, p2);
System.Console.WriteLine(e1.Union(e2).Width); // prints 351.016847158805 degrees !!