Get KML style in OpenLayers 5 - javascript

I have some code in an application that access the style of a selected feature in a KML layer. It was working in OpenLayers 3.1. I have now upgraded to 5.3.0 and it stopped working. See the relevant lines below:
var featStyle = feature.getStyleFunction().call(feature, map.getView().getResolution());
var strokeWidth = featStyle[0].getStroke().getWidth();
var strokeColor = featStyle[0].getStroke().getColor();
var fillColor = featStyle[0].getFill().getColor();
var fillOpacity = (Math.round(fillColor[3] * 100));
The line:
var featStyle = feature.getStyleFunction().call(feature, map.getView().getResolution());
Produces an error visible in the developer console:
TypeError: o.getGeometry is not a function[Learn More] KML.js:943
a KML.js:943
myFunctionName file.php:5371
onclick file.php:1
I can't find anything in the documentation or examples that shows how to properly access the KML style data for a given feature (not an entire layer/source). Is there a new way to do this or did I miss something?
Could it have to do with this?: https://github.com/IGNF/geoportal-sdk/issues/2 Plugged into Google translate it seems to say something about no longer storing style properties inside each feature but it does not seem to say where they are stored...
The KML is valid and displays on the map properly. I just can't seem to find a way to access the style data anymore.

In OpenLayers 3 and 4 a feature style function takes only a resolution argument but internally uses this so the function or call must be bound to the feature:
feature.getStyleFunction().bind(feature)(map.getView().getResolution());
or
feature.getStyleFunction().call(feature, map.getView().getResolution());
In OpenLayers 5 feature style function are similar to layer style functions and require the feature to be passed as an argument:
feature.getStyleFunction()(feature, map.getView().getResolution());

Related

Using WFS with layers with leaflet 1.0 generates an error

I'm having a problem using Leaflet 1.2.0 with WFS and control Layers, WFS works fine, however when I use it with control.layers (see code bellow) it does not work and it returns an error:
Uncaught TypeError: Can not read property 'minZoom' of undefined
If using with version 0.6 works but I can not use it because of its limitations. Can someone please help me?
The code is here in git:
https://github.com/eltonsantos/leaflet-tests/blob/master/teste14/index.html
and here in jsfiddle:
https://jsfiddle.net/eltin182/amg80r7j/3/
You are building a plain JS object:
var stComerciaisLayer = {
service: 'WFS',
// etc.
};
…and try to feed it into the Leaflet Layers Control:
var objCamadas = {
"Setores Comerciais": stComerciaisLayer
}
L.control.layers(baseLayers, objCamadas).addTo(map);
…whereas the Layers Control is supposed to handle Leaflet layers, not plain JS objects.
There should be some Leaflet plugins to handle WFS, have a look on the plugins page.

Openlayers 3 Replace a failed tile

We are running Open Layers 3.15.
Sometimes we get a dropped or failed tile.
Currently it displays nothing, (which can be confusing for our users) so we'd like to replace this with a tile that says 'no data' or something.
I've tried picking up the event and replacing the source of the tile eg
source.on('tileloaderror', function(){
source.setUrl('./images/map/failureTile.png');
});
but the problem with this is, instead of doing this on 1 tile, it does it for the entire layer, we don't want that.
Anyone know how we can do this for just the tile that failed and not the entire layer?
It's 2018, but for someone who may need this. Tested on v5.3.0
source.on('tileloaderror', function (event) {
var tileLoadFunction = function (imageTile, src) {
imageTile.getImage().src = './images/map/failureTile.png';
};
if (event.tile.tileLoadFunction_ != tileLoadFunction) {
event.tile.tileLoadFunction_ = tileLoadFunction;
event.tile.load();
}
});
This code relys on private function event.tile.tileLoadFunction_ is exposed.
Unfortunately xnakos' answer doesn't work on v5.3.0 because the event.tile.getImage() has been replaced with 1x1 canvas image by internal error handler.
Also noted, changing event.tile.src_ directly, seems to be an option, but it doesn't work either because of cache key or something.
A tile which failed to load should have a distinct class (.olImageLoadError). You can define a CSS rule not show these items.
.olImageLoadError {
display: none !important;
}
You could try this:
source.on('tileloaderror', function(event) {
event.tile.getImage().src = './images/map/failureTile.png';
});
You need the event parameter, that can get you the tile that failed, so that you can change the tile's image.
Warning: I tested the code above using tileloadend instead of tileloaderror, because my tiles never fail on me. :) I used a simulated failure rate using Math.random() and some random tiles were replaced by the image specified. I cannot think of a reason the code above would not work. If you verify it works, I will remove this warning from my answer. I tested it on OpenLayers 3.14.2 and on an OSM source.

Get JSON WebMap from ArcGIS JavaScript API Map object

I'm trying to get a WebMap object (as JSON) from a JavaScript Map object in the ArcGIS JavaScript API. Is there any way to do this within the API, without using ArcGIS.com? Ideally something like:
webMapAsJSON = map.toWebMap();
From the "Export Web Map Task" documentation in the REST API, there's this line that suggests it should exist:
"The ArcGIS web APIs (for JavaScript, Flex, Silverlight, etc.) allow developers to easily get this JSON string from the map."
However, I don't see anything in the Map object or elsewhere in the API that would do this.
You can't. At least not officially. The steps outlined below are not recommended. They use part of the ArcGIS JS library that is not part of the public API and therefore this behavior may not work in the next version of the API or they may back-patch a previous version of the API and this could stop working even on something that previously did work.
That said, sometimes you need some "future" functionality right now and this is actually a pretty straightforward way of getting what you want using the common proxy pattern
Use the undocumented "private" function _getPrintDefinition
var proxy_getPrintDefinition = printTask._getPrintDefinition;
printTask._getPrintDefinition = function() {
var getPrintDefResult = proxy_getPrintDefinition.apply(this, arguments);
//Now you can do what you want with getPrintDefResults
//which should contain the Web_Map_as_JSON
console.log(Json.stringify(getPrintDefResult));
//make sure you return the result or you'll break this print task.
return getPrintDefResult;
}
_getPrintDefinition takes the map as the first argument and a PrintParameters object as the second.
so you'll have to create a PrintTask, redefine the _getPrintDefinition function on the newly created print task as outlined above, create a PrintParameters and then run:
myPrintTask._getPrintDefinition(myMap,myPrintParameters);
The results of this on my little test are:
{"mapOptions":{"showAttribution":false,"extent":{"xmin":-7967955.990468411,"ymin":5162705.099750506,"xmax":-7931266.216891576,"ymax":5184470.54355468,
"spatialReference":{"wkid":102100,"latestWkid":3857}},"spatialReference":{"wkid":102100,"latestWkid":3857}},
"operationalLayers":[
{"id":"layer0","title":"layer0","opacity":1,"minScale":591657527.591555,"maxScale":70.5310735,"url":"http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"},
{"id":"XXX-Redacted-XXX","title":"serviceTitle","opacity":1,"minScale":0,"maxScale":0,"token":"XXX-Redacted-XXX","url":"http://XXX-Redacted-XXX/arcgis/rest/services/TestService/MapServer"},
{"id":"XXX-Redacted-XXX","opacity":1,"minScale":0,"maxScale":0,"featureCollection":{"layers":[]}},
{"id":"featureGraphics","opacity":1,"minScale":0,"maxScale":0,"featureCollection":{"layers":[]}},
{"id":"map_graphics","opacity":1,"minScale":0,"maxScale":0,"featureCollection":{"layers":[]}}
]}
if you don't need to do any operations on the web map json and just need the output then you don't even need to use the proxy pattern.
#Suttikeat Witchayakul's answer above should work if your goal is to print the map using a print service.
However, if you are trying to export the map to the web map JSON spec so that you can save it to ArcGIS Online/Portal, or re-instantiate a map object from it later, you may have some problems. This is because the web map specification is not the same as the export web map specification, which what the print task generates and sends to printing services.
Unfortunately, the ArcGIS API for JavaScript does not provide any methods to export a map object to web map JSON. This is supposed to be coming in version 4... at some point. Until then, you can use the all but abandoned cereal library. However, if your map uses layer types that are not fully supported by cereal, it may not work for you as is and you would have to extend it.
If you want to use "esri/tasks/PrintTask" to export your map, you must use "esri/tasks/PrintParameters" for execute the printTask. Just set your map object directly to printParameter.
require([
"esri/map", "esri/tasks/PrintTemplate", "esri/tasks/PrintParameters", ...
], function(Map, PrintTemplate, PrintParameters, ... ) {
var map = new Map( ... );
var template = new PrintTemplate();
template.exportOptions = {
width: 500,
height: 400,
dpi: 96
};
template.format = "PDF";
template.layout = "MAP_ONLY";
template.preserveScale = false;
var params = new PrintParameters();
params.map = map;
params.template = template;
printTask.execute(params, printResult);
});

How can I access videojs.SeekBar and ControlBar in version 5?

I want to port this plugin to the new videojs version 5. I updated most of the plugin to fit into the new videojs.extend() requirements and updated the public functions declarations.
The part I'm stuck on is when you try to load the new components into videojs:
//Range Slider Time Bar
videojs.SeekBar.prototype.options_.children.RSTimeBar = {};
//Panel with the time of the range slider
videojs.ControlBar.prototype.options_.children.ControlTimePanel = {};
If I understand it correctly (I am having some doubts), it is extending specific parts of videojs in order to contain the plugin components.
The problem is that videojs.SeekBar and videojs.ControlBar are undefined and I don't know the right way to access them in v5 (or to create these empty objects if it isn't how you do it anymore). There is also no indication in the videojs wiki article "5.0 changes details"
The full code is available here.. The faulty lines are 421 and 422
EDIT
I can get rid of the error if I replace these lines with those:
videojs.getComponent("SeekBar").prototype.options_.children.RSTimeBar = {}; //Range Slider Time Bar
videojs.getComponent("ControlBar").prototype.options_.children.ControlTimePanel = {}; //Panel with the time of the range slider
but in the plugin constructor function, I can't find back the components:
//components of the plugin
var controlBar = player.controlBar;
var seekBar = controlBar.progressControl.seekBar;
this.components.RSTimeBar = seekBar.RSTimeBar; //is undefined
When I explore the videoJs object, in my debugger, I can indeed find player.controlBar.progressControl.seekBar but it has no sub-object called RSTimebar except in options_.children. It makes sense since it is where I defined it. However, I don't know why in the version 4 I can find it but not in the version 5.
EDIT 2
I notices that the RSTimebar in options_.children array was inserted as an object instead of a pair of index/string. So I changed my lines to this:
videojs.getComponent("SeekBar").prototype.options_.children.push("RSTimeBar"); //Range Slider Time Bar
videojs.getComponent("ControlBar").prototype.options_.children.push("ControlTimePanel"); //Panel with the time of the range slider
Result: the plugin is correctly loaded with one warning per component:
VIDEOJS: WARN: The RSTimeBar component was added to the videojs object
when it should be registered using videojs.registerComponent(name,
component)
I just need to figure out the proper and simplest way to correctly load the components now.
I finally managed to update the plugin thanks to a commit where most of the work was done.

Styling a geoJSON Feature Layer in MapBox

I just started playing with MapBox and am running into a confusing issue. I'm creating a map with a geoJSON layer using this code:
var map = L.mapbox.map('map', '<MapBoxID>');
var zipLayer = L.mapbox.featureLayer('data/blah.json');
zipLayer.addTo(map);
zipLayer.setStyle({color: 'red'});
The map appears and shows the geoJSON, but it ignores the styling. When I copy that last line into the JS console in my browser, it works fine, though.
What am I missing here? Also, I've tried at least a dozen different ways of including the style in the options directly in the featureLayer() call, but nothing has worked. How do I specify the style when creating the feature layer?
I'm guessing a bit here, since I don't know the Mapbox JS very well, but it sounds a lot like an async error. Strangely, I don't see anything in the Mapbox or Leaflet APIs about a callback for this function. But, you can pass straight GeoJSON to featureLayer(), so I'd suggest using jQuery (or your XHR library of choice) to grab the data:
var map = L.mapbox.map('map', '<MapBoxID>');
var zipLayer;
$.getJSON('data/blah.json', function(data) {
zipLayer = L.mapbox.featureLayer(data);
zipLayer.addTo(map);
zipLayer.setStyle({color: 'red'});
});
Hopefully that'll do the trick.
I would go the route of using the built-in featureLayer function, then listening for it to be ready. This should help get you pointed in the right direction:
var featureLayer = L.mapbox.featureLayer()
.loadURL('/example-single.geojson')
.on('ready', function(layer) {
this.eachLayer(function(marker) {
// See the following for styling hints:
// https://help.github.com/articles/mapping-geojson-files-on-github#styling-features
marker.setIcon(L.mapbox.marker.icon({
'marker-color': '#CC0000'
}));
});
})
.addTo(map);
Have you tried adding the zipLayer after setting the style?

Categories

Resources