Pascal and Mapbox

Part 4: Pascal to JavaScript


Contents

Introduction
Setting up
Using the TMap class
BoxCast P2J: an example app
Misc. notes


Introduction

With Free Pascal's Pas2JS compiler, you can use Pascal instead of JavaScript to work with the Mapbox GL JavaScript library.

With Pas2JS, you can use the Pascal TMap class, which provides access to most of Mapbox's Map API.


Setting up

  1. Sign up for a Mapbox account if you haven't already done so. This is free to start using.

  2. Download the Pas2JS compiler (transpiler) and RTL from here.

    Unzip this file anywhere you like; no installation is needed.

  3. Download the PnJ .zip from here. This includes the Pascal interface unit for the Mapbox GL JavaScript library.

    Unzip this file anywhere you like; no installation is needed.


Using the TMap class

Keep the Mapbox GL API handy while writing your code.

Here's a very simple JavaScript app that uses the Mapbox GL library:

mapboxgl.accessToken = 'your-access-token-goes-here';
var map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox/satellite-streets-v10',
    center: [-107.74, 37.92],
    zoom: 9
});

See the Map API for information about the map options used.

To test this code, do the following:

  1. Substitute your own access token. Sign in to your Mapbox account and create an access token.
  2. Embed the script in an HTML file like in this simple example.
  3. Double-click the HTML file to open it in your browser.
Here's the Pascal equivalent of the JavaScript code. (File simple-map.pas is included in the examples/mapbox/simple-map subdirectory of the PnJ download.)

program SimpleMap;

uses
  JS,
  MapboxGL;

var
  map : TMap;
begin
  accessToken := 'your-access-token-goes-here';
  map := TMap.New(new(['container', 'map',
                       'style', 'mapbox://styles/mapbox/satellite-streets-v10',
                       'center', TJSArray._of(-107.74, 37.92),
                       'zoom', 9.0]));
end.

See the mapboxgl.pas interface unit in the packages/mapbox subdirectory of the PnJ download for how accessToken and TMap are declared external. The JavaScript implementations are actually in file mapbox-gl.js that you reference in your app's HTML file.

Note the nifty new function (in the JS unit) for creating JavaScript objects (equivalent to { } ). And the handy TJSArray._of class function for creating JavaScript arrays (equivalent to [ ] ).

To test this code, do the following:

  1. Substitute your own access token.
  2. From the simple-map subdirectory, convert the Pascal to JavaScript with the transpiler like this (substitute the path to where you unzipped Pas2JS):

    yourpath/bin/pas2js -Jirtl.js -Jc ../../../packages/mapbox simple-map.pas

  3. Double-click the index.html file in the simple-map subdirectory to open it in your browser.
To review the generated JavaScript, open file simple-map.js in a text editor or view the page source in your browser. Your code will be at the very end of the file.


BoxCast P2J: an example app

The source for BoxCast P2J, a more extensive example, is included in the PnJ download. BoxCast P2J is a Pascal version of the original Boxcast JavaScript app given here and is functionally equivalent to the original.

BoxCast P2J demonstrates how to do the following things in Pascal:

If you want to run Boxcast P2J yourself, here are the steps:

  1. Substitute your own access token in boxcast_config.pas.

  2. To test the AJAX call to the server, you'll need to compile the NDFD dynamic library (described here) and compile and set up the getforecast CGI app (described here).

    Edit boxcast_config.pas to use a non-local Web server.

    Note that you can run Boxcast P2J without doing this step, but you won't be able to retrieve any forecast data. (See the note below for more.)

  3. From the boxcast subdirectory, convert Pascal to JavaScript with the transpiler like this (substitute the path to where you unzipped Pas2JS):

    yourpath/bin/pas2js -Jirtl.js -Jc -O- -Fu../../../packages/mapbox boxcast_p2j.pas

  4. Double-click the index.html file in the boxcast subdirectory to open it in your browser.

Misc. notes

  1. The Pas2JS transpiler generates a lot of JavaScript, much more than if you wrote the JavaScript yourself. Is this an issue? Well, in the case of Mapbox apps it's probably moot. For example, the size of the generated boxcast_p2j.js is only about 1/10th the size of the mapbox-gl.js library it depends on. Reducing the size of the generated JavaScript would not have much impact on download time.

    You can reduce the amount of generated JavaScript by using the default optimizations. However, this can have unintended consequences. For example, with BoxCast P2J, pas2js strips out methods that it thinks are unused, but are called by the Mapbox library. As a result, optimizations have to be disabled with the -O- switch for this example.

  2. A downside of JavaScript is that your JavaScript is visible in the user's browser. You can mitigate this to some extent by "obfuscating" the generated JavaScript, which also has the benefit of reducing its size. You can use Google's Closure compiler service to do this, as follows:

  3. If you look at the Pascal source for the NDFD library that the getforecast CGI app calls, you'll see that all it does is make an HTTP GET request to a PHP script at https://graphical.weather.gov. Why not eliminate getforecast and the NDFD library entirely and just call this service directly from boxcast_p2j.pas? After all, you can call just about anything on the Web with the corslite function.

    Here's what BoxCast P2J does now, where fcstUrl is the CGI URL from boxcast_config.pas:

     {Make AJAX call to server, passing coordinates of clicked point.
       corslite will call the callback function with server response.}
    corslite(appSettings.fcstUrl + '?lat1=' + string(JSValue(e.lngLat.lat)) + 
              '&lon1=' + string(JSValue(e.lngLat.lng)) + '&geojson=true',
             @CorsCallback, True);
    

    Just change it to something like this:

    corslite('https://graphical.weather.gov/xml/sample_products/browser_interface/ndfdBrowserClientByDay.php?' +
              'lat=' + string(JSValue(e.lngLat.lat)) + 
              '&lon=' + string(JSValue(e.lngLat.lng)) + '&format=24+hourly',
             @CorsCallback, True);
    

    Now all you would need to do is change CorsCallback to parse the returned XML data and convert it to the GeoJSON data that BoxCast expects.

    The problem is that the weather server does not support CORS and so most browsers block this kind of cross-domain request. By making the call from a CGI app, the browser is not involved and thus can't block the request. (Note that the server where you deploy the CGI app will still need to support CORS.)


Copyright 2018 by Phil Hess.

macpgmr (at) icloud (dot) com

First posted March 16, 2018; last edited April 26, 2018.