1

We have a Leaflet map embedded into an OLE Browser Control in our client application named [T].

The HTML file we have today loads dynamic data from a local GeoJSON file that is generated on request from [T].

User interaction on the Leaflet map contains a callback to [T] application that regenerates the GeoJSON content and requests the HTML to reload.

We use Google as a Tile provider and since they changed their payment model, we are looking at minimising the amount of requests to their Tile server.

Would there be a way after sending the callback to the application [T] in reloading only the layers derived from data in the GeoJSON file preserving the tile content?

Below is our HTML script.

'T' - Leaflet

<script src="leaflet.js"></script>

<link rel="stylesheet" href="abc-tankers.css"/>
<script src="abc-geojson.js" type="text/javascript"></script>

<script>

function changeHash(obj) {
window.setTimeout(function( ) {
    window.location.hash = obj.className+','+obj.id;
    }, 500);
}


function init() {
/* routing point markers - call the GEOJSON file*/
var RPUnlockedIcon = L.icon({
      iconUrl: './images/unlocked-green-white-32x32.png',
      iconSize: [32, 32],
      iconAnchor: [16, 16],
      popupAnchor: [0, -28]
    }); 

var RPLockedIcon = L.icon({
  iconUrl: './images/locked-red-white-32x32.png',
  iconSize: [32, 32],
  iconAnchor: [16, 16],
  popupAnchor: [0, -28]
}); 

var legcount = Object.keys(abc_routing.features).length;
var leglayers = new Array(legcount);

/* firstly work through the primary routing points - these are the routing points that are found on the active route */
for (legindex = 0; legindex <= leglayers.length - 1; legindex++) {

    leglayers[legindex] = new L.layerGroup();
    var rp = L.geoJson(abc_routing_points, {
      pointToLayer: function (feature, latlng) {
        return L.marker(latlng);
      }, onEachFeature: function(feature, layer) {
        var datatable = "<div class='PopupPortTable'><div class='PopupPortTableBody'><div class='PopupPortTableRow'><div class='PopupPortTableCell'>Leg</div><div class='PopupPortTableCellContent'>" + (parseInt(feature.properties.LegIndex) + 1) + "</div></div></div>";

            if(feature.properties.Open == false && feature.properties.Advanced == false) {
                layer.setIcon(RPLockedIcon)
                layer.bindPopup("<label class='switch-lock' ><input type='checkbox'><span class='tankers-slider-lock' id='" + feature.properties.ShortCode + "-" + feature.properties.LegIndex + "' onclick='changeHash(this)'></span></label><h3>" + feature.properties.Name  + " - Closed</h3>" + datatable );
            } else if(feature.properties.Open == true && feature.properties.Advanced == false) {
                layer.setIcon(RPUnlockedIcon)
                layer.bindPopup("<label class='switch-unlock' ><input type='checkbox' checked><span class='tankers-slider-unlock' id='" + feature.properties.ShortCode + "-" + feature.properties.LegIndex + "' onclick='changeHash(this)'></span></label><h3>" + feature.properties.Name  + " - Open</h3>" + datatable);               
            }
        }, filter: function (feature, layer) {

            if (feature.properties.LegIndex==legindex && feature.properties.Advanced == false) {
                return true;
            }
            return false;
        }
    }).addTo(leglayers[legindex]); 
}

var legadvancedlayers = new Array(legcount);

/* secondly we work through the advanced routings.  these basically are all routings outside the primary ones */
for (legindex = 0; legindex <= legadvancedlayers.length - 1; legindex++) {

    legadvancedlayers[legindex] = new L.layerGroup();
    var rp = L.geoJson(abc_routing_points, {
      pointToLayer: function (feature, latlng) {
        return L.marker(latlng);
      }, onEachFeature: function(feature, layer) {
            var datatable = "<div class='PopupPortTable'><div class='PopupPortTableBody'><div class='PopupPortTableRow'><div class='PopupPortTableCell'>Leg</div><div class='PopupPortTableCellContent'>" + (parseInt(feature.properties.LegIndex) + 1) + "</div></div></div>";


            if(feature.properties.Open == false && feature.properties.Advanced == true) {
                layer.setIcon(RPLockedIcon)
                layer.bindPopup("<label class='switch-lock' ><input type='checkbox'><span class='tankers-slider-lock' id='" + feature.properties.ShortCode + "-" + feature.properties.LegIndex + "' onclick='changeHash(this)'></span></label><h3>" + feature.properties.Name  + " - Closed</h3>" + datatable );
            } else if(feature.properties.Open == true && feature.properties.Advanced == true) {
                layer.setIcon(RPUnlockedIcon)
                layer.bindPopup("<label class='switch-unlock' ><input type='checkbox' checked><span class='tankers-slider-unlock' id='" + feature.properties.ShortCode + "-" + feature.properties.LegIndex + "' onclick='changeHash(this)'></span></label><h3>" + feature.properties.Name  + " - Open</h3>" + datatable);               
            }
        }, filter: function (feature, layer) {

            if (feature.properties.LegIndex==legindex && feature.properties.Advanced == true) {
                return true;
            }

            return false;
        }
    }).addTo(legadvancedlayers[legindex]); 
}

/* route line - call the GEOJSON file*/
var routelayer = L.layerGroup();

var route = L.geoJson(abc_routing, {
  style: function(feature) {
    return {
      color: '#224A59',
       weight: 4,
       opacity: 0.9
    };
  },
  onEachFeature: function(feature, layer) {
    layer.bindPopup("<h3>Leg  " + (parseInt(feature.id) + 1) + "</h3><br>" +
      feature.properties.Description);
  }
}).addTo(routelayer);



for (legindex = 0; legindex <= leglayers.length - 1; legindex++) {

    var singleroute = L.geoJson(abc_routing, {
        style: function(feature) {
        return {
            color: 'orange',
            weight: 4,
            opacity: 0.9
        };
    },
    filter: function (feature, layer) {
        if (feature.id==legindex) {
            return true;
        }
        return false;
    }
    }).addTo(leglayers[legindex]);
}       

/* port markers - call the GEOJSON file */
var portmarkerslayer = L.layerGroup();

var PortIcon = L.icon({
  iconUrl: './images/marker-icon-2x.png',
  iconSize: [25, 41],
  iconAnchor: [16, 37],
  popupAnchor: [0, -28]
});

var portmarkers = L.geoJson(abc_marker_ports, {
  pointToLayer: function (feature, latlng) {
    return L.marker(latlng, {icon: PortIcon});
  }, 
  onEachFeature: function(feature, layer) {
      var popupContent = "<h3>" + feature.properties.PortName  + "</h3>";
      if (feature.properties) {
        popupContent += "<div class='PopupPortTable'><div class='PopupPortTableBody'><div class='PopupPortTableRow'><div class='PopupPortTableCell'>Latitude</div><div class='PopupPortTableCellContent'>" + feature.properties.Lat + "</div></div><div class='PopupPortTableRow'><div class='PopupPortTableCell'>Longitude</div><div class='PopupPortTableCellContent'>" +  feature.properties.Lon + "</div></div><div class='PopupPortTableRow'><div class='PopupPortTableCell'>Distance from Start</div><div class='PopupPortTableCellContent'>" +  feature.properties.DistanceFromStart + "</div></div></div></div>";
      }
      layer.bindPopup(popupContent);
    }         
}).addTo(portmarkerslayer);


/* firstly we set the base tile up */
var openstreetmap   = L.tileLayer('http://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png', {
            maxZoom: 18,
            attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
        });

/* this tile is actually used as an overlay  */     
var opensealayer  = L.tileLayer('https://tiles.openseamap.org/seamark/{z}/{x}/{y}.png', {
            maxZoom: 18,
            attribution: 'Map data: &copy; <a href="http://www.openseamap.org">OpenSeaMap</a> contributors'
        });

var overlays = {
    "Journey": routelayer,
    "Port Markers": portmarkerslayer,
    "OpenSeaMap" : opensealayer
};


/* add the arrays for Primary/Advanced routings */
for (legindex = 0; legindex <= leglayers.length - 1; legindex++) {
    overlays["PRP-Leg " + (legindex + 1)] = leglayers[legindex];
}

for (legindex = 0; legindex <= legadvancedlayers.length - 1; legindex++) {
    overlays["ARP-Leg " + (legindex + 1)] = legadvancedlayers[legindex];    
}


var map = L.map('map', {
    zoom: 10,
    zoomControl: false,
    layers: [openstreetmap]
});

var hashdata = ""

/* now looking into the saveddata in JSON file that was passed through the url hash - we check if any of the settings should be restored since last call */
var found = saveddata.search("xJourneyx");
if (found>0) {
    routelayer.addTo(map);
    hashdata += "xJourneyx";
}
found = saveddata.search("xPort Markersx");
if (found>0) {
    portmarkerslayer.addTo(map);
    hashdata += "xPort Markersx";
}
found = saveddata.search("OpenSeaMap");
if (found>0) {
    opensealayer.addTo(map);
    hashdata += "xOpenSeaMapx";
}

for (legindex = 0; legindex <= leglayers.length - 1; legindex++) {
    found = saveddata.search("xPRP" + (legindex + 1) + "x");
    if (found>=0) {
        leglayers[legindex].addTo(map);
        hashdata += "xPRP" + (legindex + 1 + "x");
    }   
}

for (legindex = 0; legindex <= legadvancedlayers.length - 1; legindex++) {
    found = saveddata.search("xARP" + (legindex + 1) + "x");
    if (found>=0) {
        legadvancedlayers[legindex].addTo(map);
        hashdata  += "xARP" + (legindex + 1 + "x");
    }   
}

if (hashdata!="") {
    window.location.hash = hashdata
}

/* now we work out the group for the layer control */
var baseLayers = {
    "OpenStreetMap": openstreetmap
};

L.control.layers(baseLayers, overlays, {position: 'bottomright', collapsed: true}).addTo(map);

map.fitBounds(singleroute.getBounds());

L.control.zoom({
    position:'topright'
}).addTo(map);


map.on('overlayadd', function(e) {
    var newname = e.name.replace("-Leg ","")
    window.location.hash += "x" + newname + "x";
});

map.on('overlayremove', function(e) {

    var newname = "x" + e.name.replace("-Leg ","") + "x"
    var oldhash = window.location.hash;
    var newhash = oldhash.replace(newname, "");
    window.location.hash = newhash; 

});
}

The challenge here is I can only use IE9 emulating Edge and all files are local, using FILE protocol.

  • How does the "requests the HTML to reload" feature work? Sending the URL again to the OLE browser control or something like that? What language/API/code are you using for that? – IvanSanchez Aug 21 '18 at 9:18
  • Yes, sending the url again with hash so specific layers are restored to reload again. I have Powerbuilder to work with that uses a standard dated Microsoft OLE control. – drewglew Aug 22 '18 at 2:30
  • Can you be a bit more specific? Point to the API docs of the dated OLE control perhaps? – IvanSanchez Aug 22 '18 at 7:16
  • At least some of this API is exposed to PowerBuilder. It is hard to dig out the documentation. – drewglew Aug 22 '18 at 8:05
0

This is a challenging scenario indeed. The challenge boils down to "How do I make stuff happen in a webpage inside a IE9 OLE control without reloading the whole webpage?". This is hard because the deprecated API you linked in the comments allows only to navigate to a new URL and to refresh the current page. Compare this to the evaluateJavascript() method of Android's WebView, which allows you to run arbitrary javascript code in an embedded WebView.

The only approach that I can think of is using bookmarklet-style URLs. e.g. something like this in your PowerBuilder code:

myOleIE9instance.navigate("javascript:reloadGeoJson()")

and something like this in the javascript code:

// Create a reference to each refreshable GeoJSON-based layer in the
// global scope
var myGeoJsonRoute;

// Make sure that this function is either in the global scope, or 
// reachable as window.reloadGeoJson() or similar.
function reloadGeoJson() {
    map.removeLayer(myGeoJsonRoute);

    // Load GeoJSON from the network, by any means available - jquery,
    // L.GeoJSON.AJAX, or whatever method compatible with IE9. This
    // means that the Fetch interface is not an option here.
    myGeoJsonRoute = L.GeoJSON.AJAX('abc-geojson.geojson').addTo(map);
}

If you cannot run bookmarklets in the embedded IE9 instance, then I don't think this problem has a solution (other than "throw away the obsolete IE9 control and redo it with a more modern stack").

  • Cool, I will try that. There appears no way of getting round AJAX but what you describe is way smarter than my only idea which was at same time as callback to client on the routing point click event inside popup element I call also upon the reloadGeoJson function, but in that case I don't know if and when the GeoJSON data has finished reloading. – drewglew Aug 22 '18 at 9:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.