My aim is to create a map for my work in OpenLayers 3 with several layers. One takes a basic feed from OpenStreetMaps. Another will be a transparent layer showing outlines of regions (not done yet). The third one, which is the one I'm having trouble with, shows a series of icons representing indivudal sites of interest on the map. I load the sites from a JS data stucture included as a separate script file. I have managed to get my code to add features that appear at the correct lat/lon. My next step is to get a HTML box (div) to appear in front of the map when they click on an icon (to display details of the site). However, I cannot get this to work. Apologies for my noobish coding, but it's really got me stumped and I'd really appreciate any help.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="ol.css" type="text/css">
<link rel="stylesheet" href="map.css" type="text/css">
<script src="ol.js" type="text/javascript"></script>
<script src="sites.js" type="text/javascript"></script>
<script type="text/javascript">
function buildLayerFromSites(allSites)
// allSites is just an array of data structures
{
var count, site;
// for each site in the array, make a feature
for (count=0;count<allSites.length;count++)
{
geom = new ol.geom.Point(ol.proj.fromLonLat([allSites[count].longitude,allSites[count].latitude]));
site = new ol.Feature(geom);
site.Name = allSites[count].siteName; // <-- can I assign further details in the structure like this?
var siteStyle = new ol.style.Style({
image: new ol.style.Icon ({
src: 'icon-blue.png',
scale: 0.1,
opacity: 0.9,
})
})
site.setStyle(siteStyle);
siteFeatures[count] = site;
}
siteSource = new ol.source.Vector ({
features: siteFeatures
})
siteLayer = new ol.layer.Vector({
source: siteSource
});
map.addLayer(siteLayer);
}
</script>
<title>Map</title>
</head><body>
...
<div id="map" class="map">
...
<script type="text/javascript">
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([115.51, -31.57]),
zoom: 8
})
});
buildLayerFromSites(includeSites); // adds the second layer with the sites
// click event handler, basically same as http://openlayers.org/en/v3.12.1/examples/icon.html
map.on('click', function(evt) {
var feature = map.forEachFeatureAtPixel(evt.pixel,
function(feature,layer) {
return feature;
});
if (feature) {
console.log("feature got clicked on"); // do stuff
} else {
console.log("didn't click a feature");
}
});
</script>
</body>
</html>
The map and the icons load, but when I click the icons, no matter how zoomed in or out I am, it doesn't detect a match with the icon I'm clicking on. The thing is, the event handler code is the same as in the official example, which make me think it's the way I'm creating the features. On the other hand, the features display fine as icons, its just the click event that doesn't seem to work. Similar weird stuff happens for the mouse cursor change from the example. I sorta guess it's because I don't understand the way the data/functions are structured in OL, and I find the documentation doesn't fully explain how it all works, so I've been writing snippets like this trying to take a look:
var myLayers = map.getLayers();
console.log("keys - " + myLayers.getKeys); // prints out 'function (){return Object.keys(this.B)}' - what does that mean?
var mySource = myLayers.getSource;
console.log("source" + mySource.getProjection); // doesn't work
var features = mySource.getFeatures; // says mySource is undefined
Of course it fails totally.
How do I get it to detect the clicks on the icons/features so I can tell a div to appear and display my data at the right time? Am I doing this right? What don't I understand about the way the data/functions work? I'm a big JS noob so I hope I'm not doing something stupid, but it's causing me massive problems and I could really use your help! Thanks all!
If someone else comes upon this issue: my solution was changing the Feature icon PNG image to not have transparency (no color).
If your image is PNG and has transparency,
where it is transparent it won't be clickable
(I'm on OpenLayers v5). For example, I had an image that was a circle and it was transparent inside and just colored on the outside border.
When I changed the image to something without transparency in the middle (it can still be PNG of course) everything was okay. It seems this is how OpenLayers handles pixels for images - if the map can be seen through the image, it's not a 'hover' even though you are on a Feature.
console.log("keys - " + myLayers.getKeys); // prints out 'function (){return Object.keys(this.B)}' - what does that mean?
It means you're trying to print the function itself, not the result of it's execution. Should be
console.log("keys - " + myLayers.getKeys())
Same with
mySource.getProjection();
mySource.getFeatures();
Usually if the attribute name starts with "get" and is written in camelCase - it's a function. Keep that hint in mind :)
After long hours staring at my screen and hitting my head on my desk, I've stumbled across the solution to my problem. It's a little obscure but mind-numbingly simple/stupid/silly. I thought I'd post the solution here for others.
I wrote a couple of lines of code to resize the viewport when the page is loading and again when the window is resized, so the map would adjust to the available user's browser window space. Unfortunately, as far as I can work out, OL doesn't know about it so the icons and the features are no longer in the same place after the resize. I didn't know this though, until after countless hours I was randomly clicking and resizing it detected a feature click.
Luckily there is an easy way to solve it once you figure out this is the problem. Basically just add map.updateSize() to the part of your code that resizes the viewport. So far this seems to have solved the problem.
The functions is only work on pixel of features so to solve this you can add some style with your image with fill with something like rgb(255,0,0,0.001) propert. I have tried it and i worked for me.
Related
I am trying to create a map of a small area. On this map I have 2 buttons, one is for zooming in and the other is for out. I have created 2 buttons and 1 loader, then connected these together to make it work. My code for buttons and loader:
Loader.qml
Loader {
id: mapLoader
source: "map.qml"
function reload () {
source = ""
source = "map.qml"
console.log("Map Updated")
}
}
Buttons.qml
// This button zooms in 1 unit
// get_Zoomlevel() works on C++; therefore, it isn'T affected from qml reloads.
Button {
...
onClicked: {
map.setZoomLevel(map.get_ZoomLevel() + 1)
mapLoader.reload()
}
...
}
After all, it works as expected, I can zoom in and out easily. However, since the loader loads all the map components from scratch it takes a little longer for a normal and smooth user experience. Therefore, I am looking for a better way of loading or any other advice. Thanks in advance.
I have found the answer and I want to share for the future.
I thought that zoomLevel property of QML Map was only for reading, but I noticed I can also write; therefore, when I noticed it just becomes easy.
I created a variable,
property int zoomLv = 15 // default zoom level
and whenever I zoomed in/ out using buttons I just edited this variable instead of reloading the full map Item.
onClicked: {
zoomLv += 1
}
I am trying to make a simple animation web application using paperjs, I have been reading the docs for it but I can't really grasp how this code works.
If you can provide me with headlines to search for or exactly what are the js parts that I am missing.
// To make sure the script isn't executed unless DOM is ready
window.onload = function(){
// Whenever the user clicks on a keyboard button, this event handler is executed
$('body').on('keypress', function(event) {
var path = new paper.Path.Circle({
center: [400, 200],
radius: 10,
fillColor: '#1abc9c'
});
console.log(path.fillColor); // prints undefined
console.log(path.fillColor.hue); // prints undefined
paper.view.onFrame = function(event) {
// On each frame, increment:
path.fillColor.hue += 1; // works fine !!
path.radius -= 1; // doesn't work !!
}
// starts drawing
paper.view.draw();
}
}
First question: How is path.fillColor.hue is undefined in the first console.log() line but the first line inside paper.view.onframe() works fine ?
Second question: By the same concept as path.fillColor.hue I am trying to make another animation that shrinks the radius of my circles so I am decrementing path.radius in my animation function, however, this doen't work -though no error is raised.
Notes:
1- hue is a property that changes the color of my circle, I got it from the docs.
2- I tried to put paper.view.draw() inside my paper.view.onframe() function as a solution as if it will draw a new circle with the new radius every time the paper.view.onframe() is called but this doesn't work.
3- I know that my code contains lots of repeating paper. and the paperjs docs purposes 2 conventions to avoid this but I am just trying to keep my mind on the most important things first before improving my code quality.
4- This isn't the complete code and I know that using Jquery isn't really useful as I could have replaced its use with addEventListener() but keypress event seems to have a problem so I decided to get back to it when I finish the code and eliminate jquery from my project.
The issue is that the path is no longer a "circle" once you have created it. It's just a path. You need to interact with it in a slightly more complicated way to change the radius.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<canvas id="myCanvas" resize="true" width="800" height="800"></canvas>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="paper-full.min.js"></script>
<script>
window.onload = function(){
$('body').on('keypress', function(event) {
paper.setup('myCanvas');
var path = new paper.Path.Circle({
center: [400, 200],
radius: 10,
fillColor: '#1abc9c'
});
paper.view.onFrame = function(event) {
var currentRadius = path.bounds.width / 2,
newRadius = currentRadius - 1;
path.fillColor.hue += 1;
path.scale(newRadius / currentRadius);
}
paper.view.draw();
});
}
</script>
</body>
</html>
I got the necessary info from https://groups.google.com/forum/#!topic/paperjs/xJTDWTQIFwQ which also discusses a slight problem with doing it this way that doesn't apply to your use case as presented.
What I have:
responsive image map using plugin
rollover/out mapped coordinates
an iFrame within a WordPress template that loads the page containing the image map
What I need:
The coordinates in my Javascript rollout effect code to ALSO remap according to the browser size, just like the image map itself.
Code for rollout effect looks something like this:
<SCRIPT LANGUAGE="javascript">
Image1 = new Image(687,587,27)
Image1.src = "https://www.example.com/Test/jan.png"
function zoominjan() {
document.emp.src = Image7.src; return true;
}
function zoomoutjan() {
document.emp.src = Image1.src; return true;
}
function originaljan() {
document.emp.src = Image7.src; return true;
}
</script>
In the body of the page:
<img usemap="#image-map" src="all2.png" name="emp" id="wotnot">
<map name="image-map">
<area shape="circle" coords="485,226,10" onMouseUp="zoomoutjan()" onMouseOver="zoomoutjan()" onMouseOut="originaljan()">
Any ideas how to get the rollout effect to also be responsie like the map, whilst still being functional within an iFrame in WordPress?
Much appreciation for any guidance.
SA
In the end I used a plugin and added custom code using the API.
The plugin I used was: https://codecanyon.net/item/image-map-pro-for-wordpress-interactive-image-map-builder/2826664 I will likely attempt a home-grown solution to this at some point, but the plugin worked quite well after you got past some of the more annoying installation aspects. Instructions for a full image changeout are in the documentation but there is an extra apostrophe in the example, so be careful when implementing.
I'm using the ng-map directive to display a map. The map has markers that show an infowindow whenever there's a mouseover on the marker. Sometimes however, the infowindow doesn't appear.
Other than this, I haven't been able to identify any pattern to what's happening, as the problem occurs for a different marker each time. I'm outputting data to the infowindow however the issue doesn't seem to be 'data related' as all data for the selected location seems to be correct at the point where the issue occurs.
I have a showInfo method that is being called on a mouseover like this:
showInfo(event, loc, infoWindowTemplate, map, mapsController) {
loc - data for the clicked location
infoWindowTemplate - the info window template to use (this is always the same for a particular map, however this is configurable, so if I'm showing a map for mobile, I use one infowindow template, if I'm showing a desktop map, I use a different one)
map - a reference to the NgMap object on the controller
mapsController - the controller itself (I strongly suspect that this is a bad code smell - it was the easiest way I could figure out to get reference back to the controller following the mouseover)
Here is the body of the method:
map.getMap(mapsController.mapId).then(function (myMap) {
var selectedMarker = myMap.markers["L: " + loc.position[0] + ", " + loc.position[1]];
selectedMarker.locationInfo = loc;
console.log("about to show infowindow - infoWindowTemplate = " + infoWindowTemplate);
// console output = "cached-myTemplate.html"
myMap.showInfoWindow(infoWindowTemplate, selectedMarker);
selectedMarker is definitely referring to the correct marker object. My template looks like this:
<script id="cached-myTemplate.html" type="text/ng-template">
<a class="map-location__link" href="/locations/{{anchor.locationInfo.locationId}}" target="_blank">
<img src="{{anchor.locationInfo.locationImageThumbnail}}" />
</a>
</script>
The issue seems to be that calling 'showInfoWindow' is intermittently failing somehow (although there are no errors in the console). Any comments or answers with ideas of what may be causing the issue or what else I can do to diagnose it will be appreciated!
I discovered that this is a timing issue. Delaying the 'turn' in which showInfoWindow is called (by adding a short timeout) fixed the issue:
map.getMap(mapsController.mapId).then(function (myMap) {
....
this.$timeout(function () {
dealmap.showInfoWindow(infoWindowTemplate, selectedMarker);
}, 100)
}.bind(this));
How can I easily customize OpenLayers map controls? Or at least, how can I minimize the controls' height?
Thank you.
PS. Is there any CSS override?
You can sub-class any of the openLayers controls. I just made a 'zoom-slider' by sub-classing PanZoomBar (panZoomBar.js), overriding the draw() method and commenting out all the button elements, just leaving the zoom slider.. like this:
function zoomSlider(options) {
this.control = new OpenLayers.Control.PanZoomBar(options);
OpenLayers.Util.extend(this.control,{
draw: function(px) {
// initialize our internal div
OpenLayers.Control.prototype.draw.apply(this, arguments);
px = this.position.clone();
// place the controls
this.buttons = [];
var sz = new OpenLayers.Size(18,18);
var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, 5), sz);
centered = this._addZoomBar(centered.add(0, sz.h + 5));
this._addButton("zoomout", "zoom-minus-mini.png", centered, sz);
return this.div;
}
});
return this.control;
}
var panel = new OpenLayers.Control.Panel();
panel.addControls([
new zoomSlider({zoomStopHeight:11}),
new OpenLayers.Control.LayerSwitcher({'ascending':false}),
]);
map.addControl(panel);
There is a CSS file that comes with can controls all of the CSS commands for within openlayers generally .olZoombar { here}
It is probably the easiest way to edit those sorts of things otherwise you can edit the actual .js file for the control.
If you are talking about the PanZoomBar or ZoomBar, as has been mentioned, you need to edit the zoomStopHeight. However, You do not need to edit OpenLayers.js.
new OpenLayers.Control.PanZoomBar({zoomStopHeight: 7})
You could consider trying PanZoom, which has no bar.
To minimize the ZoomBar search for zoomStopHeight in OpenLayers.js and edit it as you wish.
Further reference: Link.
Take a look here - http://geojavaflex.blogspot.com/
I am in the process of showing how to do an involved customization of the LayerSwitcher. This might give you ideas on how to do what you are after.
There is a map on the page that shows how the control works, and subsequent posts will discuss the code in detail.
If you are just interested in code see the source of the page and look for the link to CustomLayerSwitcher.js for the customized version of the switcher.