OpenLayers 4 - LayerFilter on forEachFeatureAtPixel - javascript

I have 2 vector layers of which I want only 1 to be selectable for a WFS get feature info layer. OL4 docs tell me there is an opt_layerfilter for the forEachFeatuerAtPixel function.
I’m in a similar situation like this: OpenLayers 3 hasFeatureAtPixel filter for layer.
Due to my lack of JavaScript knowledge I can’t seem to make it work with the following code in OpenLayers 4:
var displayFeatureInfo = function (pixel) {
var features = [];
map.forEachFeatureAtPixel(pixel, {
layerFilter: function (layer) {
return layer.get('name') === 'isochrones';
}
}, function (feature) {
features.push(feature);
});
if (features.length > 0) {
var info = [];
var i, ii;
for (i = 0, ii = features.length; i < ii; ++i) {
info.push('<div id="infobox">' + '<p2>' + 'Isochroon ' + features[i].get('name') + ', locatie ' + features[i].get('facilityid') + '</p2>' + '<p>' + 'aantal lopend: ' + features[i].get('n_pedestrians') + ', fiets: ' + features[i].get('n_bike') + ', ebike: ' + features[i].get('n_ebike') + '<br>' + 'speedpedelec: ' + features[i].get('n_speedpedelec') + ', auto: ' + features[i].get('n_car') + '</p>' + '</div>');
}
document.getElementById('info').innerHTML = info.join(', ') || '&nbsp';
} else {
document.getElementById('info').innerHTML = ' ';
}
};
map.on('click', function (evt) {
displayFeatureInfo(evt.pixel);
});
The layer I want to be selectable is named ‘isochrones’.
It throws me an error “d.call is not a function” when I try to click any vector layer in the map.
Could anyone point me in the right direction?

Looks like you have your args swapped.
The params for forEachFeatureAtPixel are (pixel, callback, options)
You have (pixel, options, callback)

Related

dc Line chart not highlighting properly in Firefox on legend hover

If you open this link in Chrome and hover on legends, it works fine. But if you open the same link in Firefox, something weird happens and line chart is not highlighting properly. In my personal code, I tried to explicitly add highlight class to the hovered line using the code
for (var i = 0; i < subchartIds.length; i++) {
if (subchartIds[i] === highlightedId) {
var el = chartObject.select('g.sub._' + subchartIds[i] + ' .chart-body .stack-list')._groups[0][0].childNodes[0].childNodes[0];
el.className.animVal += ' highlight';
el.className.baseVal += ' highlight';
} else {
chartObject.select('g.sub._' + subchartIds[i]).style("opacity", function () {
return 0.2;
});
}
}
where subchartIds array have IDs of number of line charts in my composite chart and highlightedId contains the ID of line chart I want to highlight i.e. line chart belonging to legend on which mouse is hovered.
After spending much time on it, I was able to solve it by first removing all the default properties which were set on path element, then reassigning required attributes myself.
for (var i = 0; i < subchartIds.length; i++) {
chartObject.select('g.sub._' + subchartIds[i]).style("opacity", function () {
return 1;
});
var localElement = chartObject.select('g.sub._' + subchartIds[i] + ' .chart-body .stack-list')._groups[0][0].childNodes[0].childNodes[0];
localElement.className.animVal = localElement.className.animVal.replace('highlight','');
localElement.className.baseVal = localElement.className.baseVal.replace('highlight','');
localElement.className.animVal = localElement.className.animVal.replace('fadeout','');
localElement.className.baseVal = localElement.className.baseVal.replace('fadeout','');
}
for (var i = 0; i < subchartIds.length; i++) {
if (subchartIds[i] === highlightedId) {
var elementToHighlight = chartObject.select('g.sub._' + subchartIds[i] + ' .chart-body .stack-list')._groups[0][0].childNodes[0].childNodes[0];
elementToHighlight.className.animVal += ' highlight';
elementToHighlight.className.baseVal += ' highlight';
chartObject.select('g.sub._' + subchartIds[i]).style("opacity", function () {
return 0.9;
});
} else {
var elementToFade = chartObject.select('g.sub._' + subchartIds[i] + ' .chart-body .stack-list')._groups[0][0].childNodes[0].childNodes[0];
elementToFade.className.animVal += ' fadeout';
elementToFade.className.baseVal += ' fadeout';
chartObject.select('g.sub._' + subchartIds[i]).style("opacity", function () {
return 0.2;
});
}
}

how to convert jsmodeller jsonData to jsmodeller Body

How to convert JSModeller jsonData for uploaded file to JSModeller body to use in JSM.ExportBodyToStl()
Requirement: option to upload stl/obj file, render it and at the end give an option to export to either stl/obj file
https://3dviewer.net/ has the option to upload files but does not have option to export to stl/obj
Problem: cannot get the body of the uploaded file.
// this code is from 3dviewer.net **impoterapp.js**
var processorFunc = JSM.ConvertFileListToJsonData;
if (isUrl) {
processorFunc = JSM.ConvertURLListToJsonData;
}
processorFunc (userFiles, {
onError : function () {
myThis.GenerateError ('No readable file found.');
myThis.SetReadyForTest ();
return;
},
onReady : function (fileNames, jsonData) {
myThis.fileNames = fileNames
// i get the jsonData here
//
// how do i convert jsonData to jsmodeller object
}
});
JSModeller has an export Option here http://kovacsv.github.io/JSModeler/documentation/demo/demonstration.html but all the examples they used here are created with generator function like JSM.LegoBrickGenerator(), JSM.ShapeGenerator (), etc
how do we generate/get/convert an stl/obj file to JSModeller Body ?
as we need to pass body to ExportBodyToStl
example http://kovacsv.github.io/JSModeler/documentation/jsmdoc/index.html#ExportBodyToStl
JSM.ExportBodyToStl (body, 'JSModelerBody', false);
JSM.ExportBodyToObj (body, 'JSModelerBody', false);
above example of exportBody is from http://kovacsv.github.io/JSModeler/documentation/demo/demonstration.html
JSModeler has an own model format called JSM.Model. You can export this model to obj and stl, or to json format. When you import something it generates the json format directly, and there is no way to convert it back to JSM.Model.
By the way I have an unpublished code which converts the json format to stl, I hope it will help you:
function ConvertJsonDataToStl (model)
{
function GetVertex (vertices, index)
{
var result = new JSM.Coord (
parseFloat (vertices[parseInt (index) * 3 + 0]),
parseFloat (vertices[parseInt (index) * 3 + 1]),
parseFloat (vertices[parseInt (index) * 3 + 2])
);
return result;
}
var stl = '';
stl += 'solid Model\n';
var meshId, triangleId, paramId, mesh, vertices, triangle;
var v0, v1, v2, normal;
for (meshId = 0; meshId < model.meshes.length; meshId++) {
mesh = model.meshes[meshId];
vertices = mesh.vertices;
for (triangleId = 0; triangleId < mesh.triangles.length; triangleId++) {
triangle = mesh.triangles[triangleId];
for (paramId = 0; paramId < triangle.parameters.length; paramId += 9) {
mesh = model.meshes[meshId];
v0 = GetVertex (vertices, triangle.parameters[paramId + 0]);
v1 = GetVertex (vertices, triangle.parameters[paramId + 1]);
v2 = GetVertex (vertices, triangle.parameters[paramId + 2]);
normal = JSM.CalculateTriangleNormal (v0, v1, v2);
stl += 'facet normal ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
stl += '\touter loop\n';
stl += '\t\tvertex ' + v0.x + ' ' + v0.y + ' ' + v0.z + '\n';
stl += '\t\tvertex ' + v1.x + ' ' + v1.y + ' ' + v1.z + '\n';
stl += '\t\tvertex ' + v2.x + ' ' + v2.y + ' ' + v2.z + '\n';
stl += '\tendloop\n';
stl += 'endfacet\n';
}
}
}
stl += 'endsolid Model\n';
return stl;
};

Looping through Google Place API Place Details

I'm working through the Google Place API documentation and I'm trying to get a script that pulls PlaceIDs from a webpage, and replace them with output from the Google Place API.
I managed to successfully get an output from multiple Place IDs by duplicating the code and changing the variable and function names, but now I'm trying to create a loop function so that I'm not duplicating code. Below is what I have, but I'm getting an error. By looking at the console, it seems to work up till the Callback function where it beaks down.
"Uncaught TypeError: Cannot set property 'innerHTML' of null
at callback (places.html:29)"
I've tried a few things, but no luck so far. Any suggestions would be appreciated. Thanks,
<body>
<div id="MY0">ChIJaZ6Hg4iAhYARxTsHnDFJ9zE</div>
<div id="MY1">ChIJT9e323V644kRR6TiEnwcOlA</div>
<script>
var request = [];
var service = [];
var div = [];
for (i = 0; i < 2; i++) {
request[i] = {
placeId: document.getElementById("MY" + i).innerHTML,
fields: ['name', 'rating', 'formatted_phone_number', 'geometry', 'reviews', 'photos'],
};
service[i] = new google.maps.places.PlacesService(document.createElement('div'));
service[i].getDetails(request[i], callback);
function callback(place, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
div[i] = document.getElementById("MY" + i);
div[i].innerHTML = "<b>" + place.name + "</b><br>" + place.rating + "<br>" + place.reviews[1].author_name + "<br>" + place.reviews[1].rating + "<br>" + place.reviews[1].text + "<br><img src='" + place.photos[0].getUrl({'maxWidth': 250, 'maxHeight': 250}) + "'>";
}
}
}
</script>
</body>
Move the callback outside of the for loop and forget about the array named div (unless you need this...if so I will rewrite). The for loop is executing before the getDetails() call returns any result, because this call is asynchronous - since you don't have much control over the Google Places callback, I would save the IDs in an array and then use them in callback, like this:
function gp_callback(place, status) {
var el = document.getElementById(window.id_set[0]); // first in first out - the for loop should populate the IDs in correct order
if (status == google.maps.places.PlacesServiceStatus.OK) {
el.innerHTML = "<b>" + place.name + "</b><br>" + place.rating + "<br>" + place.reviews[1].author_name + "<br>" + place.reviews[1].rating + "<br>" + place.reviews[1].text + "<br><img src='" + place.photos[0].getUrl({'maxWidth': 250, 'maxHeight': 250}) + "'>";
}
if (window.id_set.length > 1) {
window.id_set.splice(0, 1); // remove first element from array because has been used - now the next element is at index 0 for the next async callback
}
}
var request = [];
var service = [];
var id_set = [];
for (i = 0; i < 2; i++) {
request[i] = {
placeId: document.getElementById("MY" + i).innerHTML,
fields: ['name', 'rating', 'formatted_phone_number', 'geometry', 'reviews', 'photos'],
};
id_set.push("MY" + i); // this ensures array is populated (in proper order, b/c it tracks the execution of the for loop) for use in callback before callback is called (since getDetails() is async)
service[i] = new google.maps.places.PlacesService(document.createElement('div'));
service[i].getDetails(request[i], function(place, status) {
gp_callback(place, status);
});
}
UPDATE: More scalable and elegant answer after I had a little more time to think about it.
<div id="MY0" class="gp_container">ChIJaZ6Hg4iAhYARxTsHnDFJ9zE</div>
<div id="MY1" class="gp_container">ChIJT9e323V644kRR6TiEnwcOlA</div>
.
.
.
<div id="MYN" class="gp_container">fvbfsvkjfbvkfvb</div> // the nth div
<script>
function populate_container(place, status, container_id) {
var el = document.getElementById(container_id);
if (status == google.maps.places.PlacesServiceStatus.OK) {
el.innerHTML = "<b>" + place.name + "</b><br>" + place.rating + "<br>" + place.reviews[1].author_name + "<br>" + place.reviews[1].rating + "<br>" + place.reviews[1].text + "<br><img src='" + place.photos[0].getUrl({'maxWidth': 250, 'maxHeight': 250}) + "'>";
}
}
function call_service(id_request_map) {
var i, container_id, request,
service_call = function(container_id, request) {
var service = new google.maps.places.PlacesService(document.createElement('div'));
service.getDetails(request, function(place, status) {
populate_container(place, status, container_id);
});
};
for(i in id_request_map) {
service_call(i, id_request_map[i]);
}
}
$(document).ready(function() {
var request, container_id,
id_request_map = {},
container_length = document.getElementsByClassName("gp_container").length,
i = 0;
for (; i < container_length; i++) {
container_id = "MY" + i;
request = {
placeId: document.getElementById(container_id).innerHTML,
fields: ['name', 'rating', 'formatted_phone_number', 'geometry', 'reviews', 'photos'],
};
id_request_map[container_id] = request; // build the association map
}
call_service(id_request_map);
});
</script>

How to toggleClass with SignalR hub.server?

I am currently learning SignalR with .Net MVC and following a tutorial to work on a simple app. Right now it is working alright, but I am having trouble understanding some part and also if possible, want to sort of enhance it.
Plane Seats Tutorial link
Right now the app is working as when a user clicks on a seat, it reserves it. And there is no going back. I want to implement like a toggle, where if the user wants to change seat, he gets to unreserve his selected seat, and then be free to reserve another one. I am not being able to do it with myHub.server.selectSeat(userId, $(this).toggleClass(settings.selectingSeatCss));. Whenever I click on a seat, it gives me this error in the Dev tools
Uncaught: Converting circular structure to JSON
var settings = {
rows: 5,
cols: 15,
rowCssPrefix: 'row-',
colCssPrefix: 'col-',
seatWidth: 35,
seatHeight: 35,
seatCss: 'seat',
selectedSeatCss: 'selectedSeat',
selectingSeatCss: 'selectingSeat'
};
$(function() {
//// Start the hub
window.hubReady = $.connection.hub.start();
});
$.connection.hub.start().done(function() {
// Call the server side function AFTER the connection has been started
myHub.server.createUser();
//invoke for the user data
myHub.server.populateSeatData();
});
// Seat selection
$('.' + settings.seatCss).click(function() {
if ($(this).hasClass(settings.selectedSeatCss)) {
alert('Sorry, this seat has been already reserved');
} else {
//$(this).toggleClass(settings.selectingSeatCss);
//myHub.server.selectSeat(userId, $(this).toggleClass(settings.selectingSeatCss));
myHub.server.selectSeat(userId, $(this)[0].innerText);
}
});
// Client method to broadcast the message
myHub.client.createUser = function(message) {
userId = message;
};
//get seats data
myHub.client.populateSeatData = function(message) {
var parsedSeatsData = JSON.parse(message);
$('li.seat').removeClass(settings.selectedSeatCss);
$.each(parsedSeatsData, function(index, value) {
$("a:contains('" + value.seatnumber + "')").parent("li").toggleClass(settings.selectedSeatCss);
});
};
// Client method to broadcast the message as user selected the seat
myHub.client.selectSeat = function(message) {
var parsedSeatData = JSON.parse(message);
$("a:contains('" + parsedSeatData.seatnumber + "')").parent("li").toggleClass(settings.selectedSeatCss);
};
And can anyone please briefly explain what is str.push doing in this block of code? What is it exactly pushing into the array?
var init = function(reservedSeat) {
var str = [],
seatNo, className;
for (i = 0; i < settings.rows; i++) {
for (j = 2; j < settings.cols; j++) {
seatNo = (i + j * settings.rows + 1);
className = settings.seatCss + ' ' + settings.rowCssPrefix + i.toString() + ' ' + settings.colCssPrefix + j.toString();
if ($.isArray(reservedSeat) && $.inArray(seatNo, reservedSeat) != -1) {
className += ' ' + settings.selectedSeatCss;
}
str.push('<li class="' + className + '"' + 'style="top:' + (i * settings.seatHeight).toString() + 'px;left:' + (j * settings.seatWidth).toString() + 'px">' + '<a title="' + seatNo + '">' + seatNo + '</a>' + '</li>');
}
}
$('#place').html(str.join(''));
};
I had to use a toggleSeat() function instead of just using toggleClass.
public void toggleSeat(int userId, int seatNumber)
{
PlaneSeatArrangment mySeat = allSeats.Where(s => s.SeatNumber == seatNumber).FirstOrDefault();
var retunData = JsonConvert.SerializeObject(mySeat);
if (mySeat != null && userId == mySeat.UserId)
..............
}

Calling geonames to find nearby streets

I'm trying to get a list of nearby streets given a LatLng value using the geonames web service. I can get nearby wikopedia articles but am unable to get a list of street names using their findNearbyStreetsOSM method. This is what i have:
I'm blind and using a screenreader. Hopefully the code is indented correctly.
//geonames API function
//feed in the geonames method i.e. findNearbyWikipedia, findNearbyStreetsOSM + the long and lat values + the name of the div you want the results to be displayed in
function FetchDataFromGeoNames(geonamesMethod, latitude, longitude, divToOutputResultsTo) {
var geonamesAPIKey = "my_API_key);
var apiUrl = "http://api.geonames.org/";
var radius = 1;
var request = apiUrl + geonamesMethod + "JSON?lat=" + latitude + "&lng=" + longitude + "&username=" + geonamesAPIKey;
if (geonamesMethod == 'findNearbyWikipedia') {
request += "&radius=" + radius + "&maxRows=5&country=UK";
}
request += '&callback=?';
//alert(request);
//pass the request onto geonames API
$.getJSON(request, {}, function(res) {
if (res.hasOwnProperty("status")) {
$("#divToOutputResultsTo").html("Sorry, I failed to work because: " + res.status.message);
return;
}
var s = "";
//loop through the results
for (var i = 0; i < res.geonames.length; i++) {
//alert(JSON.stringify(res));
//if find wikopedia request then
if (geonamesMethod == 'findNearbyWikipedia') {
s += "<p><h2>" + res.geonames[i].title;
} else if (geonamesMethod == 'findNearbyStreetsOSM') {
s += "<p><h2>" + res.geonames[i].name;
}
if (geonamesMethod == 'findNearbyWikipedia') {
if(res.geonames[i].hasOwnProperty("thumbnailImg")) s += "<img src='"+res.geonames[i].thumbnailImg+"' align='left'>";
if (!!res.geonames[i].feature && res.geonames[i].feature != "undefined") s += '<br />Feature: ' + res.geonames[i].feature;
s += '<br />' + res.geonames[i].summary;
s += "<br clear='left'><a href='http://"+res.geonames[i].wikipediaUrl+"'>[Read More]</a></p>";
} else if (geonamesMethod == 'findNearbyStreetsOSM') {
s += '<br />Highway: ' + res.geonames[i].highway;
}
}
//concatonate the results
if (s != "") {
if (geonamesMethod == 'findNearbyWikipedia') {
s = "<h2>Nearby Wikopedia</h2>" + s;
} else if (geonamesMethod == 'findNearbyStreetsOSM') {
s = "<h2>Nearby streets (OSM)</h2>" + s;
}
//display the results on screen
$(divToOutputResultsTo).html(s);
}
});
}
Thanks,

Categories

Resources