I'm trying to get dynamically generated icons from a specific host.
I try to define a new icon class, and change iconUrl and iconRetinaUrl like so:
var opts = L.Icon.Default.prototype.options;
opts.iconUrl = 'http://localhost:3001/marker?c=blue';
opts.iconRetinaUrl = 'http://localhost:3001/marker?c=blue';
var houseIcon = L.Icon.Default.extend({ options: opts });
and later use the icon.
var theIcon = new houseIcon();
var marker = L.marker([loc.lat, loc.lon], { 'icon': theIcon }).addTo(map);
All I get is missing images, and see that the browser is trying to get the icons from
https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/images/http://localhost:3001/marker?c=blue
Obviously with a 404 answer.
I cannot see something like a baseUrl option in the documentation that for iconUrl says "The URL to the icon image (absolute or relative to your script path)."
How can I get icons from a different location than the script? Is it possible at all?
Related
tl;dr
popup.openOn(map) only works in firefox, not in chrome or edge. Popups on markers work flawlessly.
Lengthy version:
I'm kind of abusing leaflet.draw to let users insert an image overlay. I do this by letting the user draw a rectangle, grab the relevant corners and use it to place the image overlay. The image itself is uploaded by the user via a some form fields. These are shown in the popup I open on the center position of the drawn rectangle. The code looks like this, the important part is the last line:
map.on('draw:created', function (event) {
if (event.layerType === 'rectangle') {
insertImage(event);
}
}
function insertImage(event){
layer = event.layer,
feature = layer.feature = layer.feature || {}; // Intialize layer.feature
feature.type = feature.type || "Feature"; // Intialize feature.type
props = feature.properties = feature.properties || {}; // Intialize feature.properties
props.imageURL = ""; // Define the necessary feature properities -> imageURL and zoomLevels
props.zoomLevel = minZoom+1;
props.endZoomLevel = maxZoom;
var editablePopup = L.responsivePopup({maxWidth: "auto"}); // create the popup and afterward the content for it
imageBounds = layer.getBounds(); // get the bounding box of the rectangle aka its NE and SW corner
content = imageForm("", props.zoomLevel, props.endZoomLevel, imageBounds, "create"); //create the form in the popup
editablePopup.setContent(content); // add the content to the popup
event.layer.addTo(map); //we need to add the event layer to the map in order be use the getCenter method to place the popup
editablePopup.setLatLng(event.layer.getCenter()); //the popup will open at the center of the drawn rectangle
map.removeLayer(event.layer); //now we can get rid of the layer and remove it from the map
editablePopup.openOn(map); //open the popup
};
It works perfectly fine in Firefox, but in Chrome or Edge no popup opens and no error or warning is shown in the console.
Notes:
I do use the responsivePopup plugin, but already tested it with the regular L.popup and got the
same result.
Popups on markers work fine in all browsers.
If I replace the last line with layer.bindPopup(editablePopup); it stops working alltogether, even in Firefox.
I'm new to leaflet and am trying to implement a set of markers with different CSS-styles.
So, I am aware that after adding a marker to a map I can access different CSS-attributes by calling getElement() on my marker for example:
marker.addTo(map);
marker.getElement().style.borderColor = '#000';
This works just fine, but when adding a marker to a layer, this can no longer be used since a TypeError occurs (getElement() is undefined). Here is the example code where the error occurs:
myLayer.addLayer(marker);
marker.getElement().style.borderColor = '#000';
Am I overlooking a simpler way to set CSS-Attributes for markers and divicons that are added to layers or is there a similar way to access layer-added markers and divicons in JavaScript?
So I found a solution that is working for me.
The idea is to extend the function that is used to create the icon.
Last answer here github.com/Leaflet/Leaflet/issues/5231 helped a lot.
var borderSize = ...;
L.DivIcon.Custom = L.DivIcon.extend({
createIcon: function(oldIcon) {
var icon = L.DivIcon.prototype.createIcon.call(this, oldIcon);
icon.style.borderSize = borderSize;
...
return icon;
}
})
var icon = new L.DivIcon.Custom({
...
});
var ll = L.latLng(entry.Longitude, entry.Latitude);
var marker = L.marker(ll, {
icon: icon
})
this.myLayer.addLayer(marker);
Welcome to SO!
When not added onto a map (since your parent myLayer may not be added to the map itself), a marker does not have any element.
If you do not need to change too many styles individually and dynamically, you might rather use the className option of your Icon / DivIcon.
I want to create xul based frame in one of my firefox extension. The frame should look like https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/ui_frame
or
how to use below node js code in xul:
var { Frame } = require("sdk/ui/frame");
var frame = new Frame({
url: "./city-info.html"
});
In node.js, its working fine, but I dont know how to create the same thing with xul. Anybody can help?
thanks in advance.
You have provided very little detail as to what you desire. If all that you desire is to create an <iframe> in which you can load HTML content, then something along the lines of the following will do so:
//The URL of the HTML you desire to load.
let chromeUrl = '[Some URL here]';
//Whatever element you want the iframe placed under.
let parentEl = document.getElementById('foo');
//* Overlay and bootstrap (from almost any context/scope):
Components.utils.import('resource://gre/modules/Services.jsm');//Services
let activeWindow = Services.wm.getMostRecentWindow('navigator:browser');
//*/
let mainDocument = activeWindow.document;
//Create the <iframe> use
//mainDocument for the XUL namespace.
let iframeEl;
if(options.useBrowser){
iframeEl = mainDocument.createElement('browser');
} else {
iframeEl = mainDocument.createElement('iframe');
}
iframeEl.id = id;
iframeEl.setAttribute('src',chromeUrl);
iframeEl.setAttribute("tooltip", "aHTMLTooltip");
iframeEl.setAttribute("autocompleteenabled", true);
iframeEl.setAttribute("autocompletepopup", "PopupAutoComplete");
iframeEl.setAttribute("disablehistory",true);
iframeEl.setAttribute('type', 'content');
parentEl.appendChild(iframeEl);
The above code was taken from my answer to Firefox SDK Add-on with a sidebar on both the right and left at the same time, which creates sidebars. One option of how those sidebars are created is to have them contain an <iframe>.
Finally I got the answer:
let chromeUrl = 'YOUR HTML PAGE URL';
Components.utils.import('resource://gre/modules/Services.jsm');//Services
let activeWindow = Services.wm.getMostRecentWindow('navigator:browser');
//*/
let mainDocument = activeWindow.document;
let iframeEl;
iframeEl = mainDocument.createElement('iframe');
iframeEl.id = "d";
iframeEl.setAttribute('src',chromeUrl);
iframeEl.setAttribute("tooltip", "aHTMLTooltip");
iframeEl.setAttribute("autocompleteenabled", true);
iframeEl.setAttribute("autocompletepopup", "PopupAutoComplete");
iframeEl.setAttribute("disablehistory",true);
iframeEl.setAttribute('type', 'content');
iframeEl.setAttribute('height', '32px');
window.document.documentElement.appendChild(iframeEl);
Another developer created our original map but I'm tasked with making some changes. One of these is making sure the activated marker is brought to the front when clicked on (where it is partially overlapped by other markers).
The developers have used mapbox 2.2.2.
I have looked at leafletjs's docs, have followed some instructions on other posted solutions (e.g. solution one and solution two). Neither of these makes any difference.
Examining the marker in Chrome's console I can see the value of options.zIndexOffset is being set (10000 in my test case). I've even set _zIndex to an artificially high value and can see that reflected in the marker's data structure. But visually nothing is changing.
This is how the map is set up initially. All features are from a single geojson feed:
L.mapbox.accessToken = '<access token here>';
var map = L.mapbox.map('map', 'map.id', {
}).setView([37.8, -96], 3);
var jsonFeed, jsonFeedURL;
var featureLayer = L.mapbox.featureLayer()
.addTo(map)
.setFilter(function (f) {
return false;
});
$.getJSON(jsonFeedURL, function (json) {
jsonFeed = json;
jsonFeedOld = json;
// Load all the map features from our json file
featureLayer.setGeoJSON(jsonFeed);
}).done(function(e) {
// Once the json feed has loaded via AJAX, check to see if
// we should show a default view
mapControl.activateInitialItem();
});
Below is a snippet of how I had tried setting values to change the z-index. When a visual marker on the featureLayer is clicked, 'activateMarker' is called:
featureLayer.on('click', function (e) {
mapControl.activateMarker(e);
});
The GEOjson feed has urls for the icons to show, and the active marker icon is switched to an alternative version (which is also larger). When the active feature is a single Point I've tried to set values for the marker (lines commented out, some of the various things I've tried!)
activateMarker: function (e) {
var marker = e.layer;
var feature = e.layer.feature;
this.resetMarkers();
if (feature.properties.hasOwnProperty('icon')) {
feature.properties.icon['oldIcon'] = feature.properties.icon['iconUrl'];
feature.properties.icon['iconUrl'] = feature.properties.icon['iconActive'];
feature.properties.icon['oldIconSize'] = feature.properties.icon['iconSize'];
feature.properties.icon['iconSize'] = feature.properties.icon['iconSizeActive'];
}
if (feature.geometry.type == 'Point') {
marker.setZIndexOffset(10001);
marker.addTo(featureLayer);
}
//featureLayer.setGeoJSON(jsonFeed);
}
Any advice would be greatly appreciated! I'm at the point where I don't know what else to try (and that's saying something).
What probably happens is that you just flush your markers with the last call to .setGeoJSON():
If the layer already has features, they are replaced with the new features.
You correctly adjust the GeoJSON data related to your icon, so that when re-created, your featureLayer can use the new values to show a new icon (depending on how you configured featureLayer).
But anything you changed directly on the marker is lost, as the marker is removed and replaced by a new one, re-built from the GeoJSON data.
The "cleanest" way would probably be to avoid re-creating all features at every click.
Another way could be to also change something else in your GeoJSON data that tells featureLayer to build your new marker (through the pointToLayer option) with a different zIndexOffset option.
On my Google Site I've inserted an Apps Script Gadget (by pasting the URL of an Apps Script that I published as a service). This Apps Script allows the user to enter a value (their 'Blow Number') and view the corresponding data (based on API calls to my Google Fusion tables).
Right now, the script returns 3 hyperlinks:
Click here for a table of Blow Number 1
Click here for a chart of Blow Number 1
Click here for a map of Blow Number 1
This is because my script function getblowdetails has 3 app.createAnchor variables. Instead of having the script return 3 hyperlinks (that the user has to click on and view the resulting URL in a new window), I would like for the script to automatically invoke the 3 URLs and display the table, chart, and map in panels on the same page.
So the user would enter their Blow Number and press enter. They would then view the table, chart, and map directly below the text box on the same web page.
Please see the Code that I've included below and advise...Thanks for the help- I'm an apps-script novice so a thorough and understand-able response is greatly appreciated!
Code
Note: I've removed the URLs from the createAnchor variables because I'm only allowed to include 2 links in the post, but you can see them by going to www.OnSiteBAC.com/ViewMyBlows and entering Blow Number = 1...then click on the hyperlinks.
function doGet() {
var app = UiApp.createApplication();
// Create input boxes, buttons, labels, and links
var textBoxA = app.createTextBox().setId('textBoxA').setName('textBoxA').setFocus(true);
var buttonA = app.createButton('Get Blow Details').setEnabled(false);
var label = app.createLabel('Please enter your Blow Number here');
var link = app.createAnchor('where can I find my Blow Number?', 'http://www.onsitebac.com');
// Create a handler to call the getblowdetails function.
// A validation is added to this handler so that it will only invoke 'getblowdetails' if textBoxA contains a number
var handler = app.createServerClickHandler('getblowdetails').validateNumber(textBoxA).addCallbackElement(textBoxA);
// Create a handler to enable the button if all input is legal
var onValidInput = app.createClientHandler().validateNumber(textBoxA).forTargets(buttonA).setEnabled(true).forTargets(label, link).setVisible(false);
// Create a handler to mark invalid input in textBoxA and disable the button
var onInvalidInput1 = app.createClientHandler().validateNotNumber(textBoxA).forTargets(buttonA).setEnabled(false).forTargets(textBoxA).setStyleAttribute("color", "red").forTargets(label, link).setVisible(true);
// Create a handler to mark the input in textBoxA as valid
var onValidInput1 = app.createClientHandler().validateNumber(textBoxA).forTargets(textBoxA).setStyleAttribute("color", "black");
// only fire ServerHandler for onKeyUp if it passes validation
var textBoxHandler = app.createServerHandler('textBoxHandlerFunction');
// Add all the handlers to be called when the user types in the text boxes
textBoxHandler.addCallbackElement(textBoxA);
textBoxA.addKeyUpHandler(onInvalidInput1);
textBoxA.addKeyUpHandler(onValidInput1);
textBoxA.addKeyUpHandler(onValidInput);
textBoxA.addKeyUpHandler(textBoxHandler);
buttonA.addClickHandler(handler);
app.add(textBoxA);
app.add(buttonA);
app.add(label);
app.add(link);
return app;
}
function textBoxHandlerFunction(e) {
var app = UiApp.getActiveApplication();
if (e.parameter.keyCode == 13)
{
app = getblowdetails(e);
}
return app;
}
function getblowdetails(e) {
var app = UiApp.getActiveApplication();
var panel2 = app.createVerticalPanel();
var link2 = app.createAnchor ().setStyleAttribute("color", "green");
var panel3 = app.createVerticalPanel();
var link3 = app.createAnchor ();
var panel4 = app.createVerticalPanel();
var link4 = app.createAnchor ();
panel3.add(link3);
app.add(panel3);
panel4.add(link4);
app.add(panel4);
return app;
}
I don't think you'll be able to actually download the result and show it. So, there's no easy solution.
But you can build that table and chart on Apps Script easily (assuming you can already fetch the info from tables using its API).
The last issue is the map. On Apps Script you can only create static maps, meaning, an image. You can add custom markers and polygons, set the zoom, etc. But in the end it's a photo. The user will not be able to drag it around or use the map as an embedded google map as one would expect.