Select to view content in your preferred language

Arcade expression that returns Google Maps link with same map extent

267
9
a month ago
Labels (2)
OvidiuAlexa
Emerging Contributor

Hello everybody,

I am in the process of building a web map in Experience Builder and I would to let the users open a new Google Maps tab with the same map extent and possibly zoom level.

I reckon the best way to do this is via a custom widget. However, as this is not currently possible on my side, I thought that having an URL in the pop-up of the selected feature can get the job done in a similar way.

As such, I went to the map in Map Viewer (the newest version) and inserted the following Arcade expression in the pop-up settings of my layer:

 

var centroid = Centroid(Geometry($feature))
var x = centroid.x
var y = centroid.y

//MISSING CODES: Convert from EPSG:27700 to EPSG:4326 to correctly display the map in Google Maps

var URL = "https://www.google.com/maps/@" + x + "," + y + ",4000m"

 

 

This is a very simple code. However, as my map is in EPSG:27700 (British National Grid), I would need to convert the x and y values into EPSG:4326, which is the projection system used in Google Maps. Since Arcade does not support reprojection natively, I would need to find another way to achieve the wanted result.

I had a look online and found a more complex expression in this post: Create URL link to Google Street View for arcgis o... - Esri Community, which apparently worked for some users. However, when I tried to run it on my end, Arcade didn't recognise some of its functions.

How would it be possible to make Arcade correctly construct the URL? Also, more generally, is there another way of letting users open Google Maps without recurring to custom widgets and Arcade?

Many thanks!

9 Replies
Brian_McLeer
Frequent Contributor

I used this one a few years ago and it worked, but not sure if it will help with the conversion. 

 

https://support.esri.com/en-us/knowledge-base/how-to-create-hyperlinks-to-google-maps-based-on-latit...

Brian
OvidiuAlexa
Emerging Contributor

Hi Brian,

Many thanks for your reply.

I went through the guide you provided but I don't have the coordinates stored in the attribute table of my layer. Unfortunately, I can't directly add them either as the layer is secured. On top of that I would need to convert them into EPSG:4326 which is proving to be tricky.

However, it's good to know it's possible to create Google Maps URLs using attributes.

All the best!

DavidPike
MVP Frequent Contributor

You might be stuck with coding-in the Helmert transformation 😬

Chat GPT so needs tested, and has probably dreamt up half of the Function and Constant names.  But maybe a starter for 10.

 

// Input BNG coordinates (replace with actual values)
var x_bng = 532256;  // Easting (X)
var y_bng = 180749;  // Northing (Y)

// Constants for conversion (simplified Helmert transformation parameters)
var a = 6377563.396;   // Semi-major axis of Airy 1830 ellipsoid
var b = 6356256.909;   // Semi-minor axis
var e2 = (a^2 - b^2) / a^2;  // Eccentricity squared

// Origin of BNG grid
var n0 = -100000;      // Northing of true origin
var e0 = 400000;       // Easting of true origin
var phi0 = 49 * (PI / 180);  // Latitude of true origin (degrees to radians)
var lambda0 = -2 * (PI / 180); // Longitude of true origin (degrees to radians)
var f0 = 0.9996012717; // Scale factor on the central meridian

// Calculate latitude and longitude in radians
var n = (a - b) / (a + b);
var n2 = n * n;
var n3 = n * n2;

var phi = phi0;
var m = 0;
var max_iter = 1000;

// Iteratively calculate latitude (phi) from northing (y_bng)
do {
    var phi_prev = phi;
    m = b * f0 * (
        (1 + n + (5/4)*n2 + (5/4)*n3) * (phi - phi0) -
        (3*n + 3*n2 + (21/8)*n3) * sin(phi - phi0) * cos(phi + phi0) +
        ((15/8)*n2 + (15/8)*n3) * sin(2*(phi - phi0)) * cos(2*(phi + phi0)) -
        (35/24)*n3 * sin(3*(phi - phi0)) * cos(3*(phi + phi0))
    );
    phi = (y_bng - n0 - m) / (a * f0) + phi;
} while (abs(phi - phi_prev) > 0.00001 && --max_iter > 0);

// Calculate longitude
var sin_phi = sin(phi);
var cos_phi = cos(phi);
var tan_phi = tan(phi);

var nu = a * f0 / sqrt(1 - e2 * sin_phi^2);
var rho = a * f0 * (1 - e2) / pow(1 - e2 * sin_phi^2, 3/2);
var eta2 = nu / rho - 1;

var x_diff = x_bng - e0;
var lat = phi - (tan_phi / (2 * rho * nu)) * (x_diff^2 - (5 + 3*tan_phi^2 + eta2 - 9*eta2*tan_phi^2) * x_diff^4 / 24);
var lon = lambda0 + (1 / (cos_phi * nu)) * x_diff - (1 / (6 * cos_phi * nu^3)) * (1 + 2*tan_phi^2 + eta2) * x_diff^3;

// Convert latitude and longitude from radians to degrees
var latitude = lat * (180 / PI);
var longitude = lon * (180 / PI);

// Output the WGS84 coordinates
return "Lat: " + latitude + ", Lon: " + longitude;

 

 

OvidiuAlexa
Emerging Contributor

Hi David,

Thank you very much for the codes you provided.

I tried to run them but the but the do part (from line 27 to 36) is not recognised by Arcade. I noticed that ChatGPT often inserts JavaScript functions into Arcade expressions, which is not good as many of them don't run in Arcade.

I will see if there is away to achieve the same result without the do bit.

All the best.

0 Kudos
DavidPike
MVP Frequent Contributor

Ah yeh certainly needs extensive debugging and putting into Arcade syntax.  More to give an idea of utilising the Helmert transform.

Sknittel_Geauga
New Contributor

Using the link you provided, I updated the code given specifically line 29. I have it linked to a parcel layer, and it typically works for opening nearby streetview in a new browser tab - however larger parcels sometimes have the point generated too far away from a street centerline for Google to snap to it. 

var PointGeometry = Centroid(Geometry($feature));

var ArcadeX = PointGeometry.x;
var ArcadeY = PointGeometry.y;
var ArcadeSr = PointGeometry.spatialReference.wkid;
var Latitude, Longitude;

function AuxSphereToLatLon(x, y) {
  Console("Converting...");
  // Conversion based on http://dotnetfollower.com/wordpress/2011/07/javascript-how-to-convert-mercator-sphere-coordinates-to-latitude-and-longitude/
  var rMajor = 6378137;
  var shift = PI * rMajor;
  Longitude = x / shift * 180.0;
  Latitude = y / shift * 180.0;
  Latitude = 180 / PI * (2 * Atan(Exp(Latitude * PI / 180.0)) - PI / 2.0);
}

if (ArcadeSr == 4326) {
  Console("4326 Spatial Reference - No Conversion Necessary");
  Latitude = ArcadeY;
  Longitude = ArcadeX;
} else if (ArcadeSr == 102100) {
  Console("102100 Spatial Reference - Conversion Necessary");
  AuxSphereToLatLon(ArcadeX, ArcadeY);
} else {
  Console(ArcadeSr + " Spatial Reference is not supported - currently works with Web Maps where the basemap is in WGS84 (4326) or Web Mercator Auxiliary Sphere 102100");
}

var url = "https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=" + text(Latitude) + "," + text(Longitude);
return url;

 

OvidiuAlexa
Emerging Contributor

Hi Sknittel and many thanks for changing the codes for me.

This expression runs successfully. However, as my projection system is EPSG:27700, the URL is not constructed correctly.

The tricky part is getting the conversion, setting the point and constructing the URL work just fine.

I will inspect this further and let you know if I'm able to update the codes for my case.

0 Kudos
jcarlson
MVP Esteemed Contributor

Is the map itself in that projection, or just the feature layer? And if it's the map, does the purpose of this map require you to use 27700?

The default web map in AGOL is Web Mercator, unless you've used a custom basemap to change that.

The Web Mercator → Lat/Lon function is considerably simpler, if your use case can tolerate the map being in Web Mercator.

function WebMercatorToWGS84 (x, y) {
    var lon = (x / 20037508.34) * 180;
    var lat = (y / 20037508.34) * 180;
    lat = 180/PI * (2 * Atan(Exp(lat * PI / 180)) - PI / 2);
    return {
        y: lat,
        x: lon
    }
}

var c = Centroid($feature)
var coords = WebMercatorToWGS84(c.x, c.y)
var url = `https://www.google.com/maps/@${coords['x']},${coords['y']},4000m`

 

- Josh Carlson
Kendall County GIS
OvidiuAlexa
Emerging Contributor

Hi Josh and many thanks for your reply.

I confirm both the map and the feature layer are in the EPSG:27700 projection. We are required to keep it like that as this specific user case requires the British National Grid view.

I tried the expression you suggested and it works perfectly fine. The only problem is that the Google Maps view opens on Null Island (just to the south-west of Africa, near the equator). I reckon this is because the codes assume my map is in Web Mercator and therefore the lat/long in the output are wrong.

I'm wondering whether the only way to calculate the correct lat/long is via the Helmert transformation as suggested by @DavidPike. Otherwise I can just build a custom widget for Experience Builder as this allows JavaScript coding.

Thank you again.

All the best.

0 Kudos