i'm using OpenLayers to display custom OSM maps on my website.
I've some points to respect: the map have to be fix (meaning that we can't drag it or zoom it).
I have a problem with the zoom, i can't manage to disable zoom with the mouse. Does anyone has a tip?
map = new OpenLayers.Map('map');
map.events.remove("move");
map.events.remove("movestart");
map.events.remove("moveend");
map.events.remove("zoomend");
map.events.remove("mouseover");
map.events.remove("mouseout");
map.events.remove("mousemove");
map.events.remove("zoomstart");
var nav = new OpenLayers.Control.Navigation({
defaultDblClick: function(event) { return ; }
});
map[index].addControl(nav);
Also, if someone has a tip to remove all Navigation events easier than that, it would be greatly appreciated.
Disable the default controls on your map by passing an empty array:
var map = new OpenLayers.Map('map', { controls: [] });
For OpenLayers3 the interaction array also needs to be empty.
var map = new ol.Map({
controls: [],
interactions: []
});
Simplifying approach of Mahdi results in
var i, l, c = map.getControlsBy( "zoomWheelEnabled", true );
for ( i = 0, l = c.length; i < l; i++ ) {
c[i].disableZoomWheel();
}
This way disabling zoom on mouse wheel doesn't require to customize options on constructing map e.g. by creating map without any control (though this was somewhat requested by Lght). In addition re-enabling zoom works equivalently.
In addition, by searching controls matching enabled property zoomWheelEnabled rather than class name it's supporting custom controls derived from OpenLayers.Control.Navigation.
You can do the following also:
map = new OpenLayers.Map({
// options here ...
}
var Navigation = new OpenLayers.Control.Navigation({
'zoomWheelEnabled': false,
'defaultDblClick': function ( event ) {
return;
}
});
map.addControl(Navigation);
var NavigationControls = map.getControlsByClass('OpenLayers.Control.Navigation')
, i;
for ( i = 0; i < NavigationControls.length; i++ ) {
NavigationControls[i].disableZoomWheel();
}
Found here.
For other options like disable dragging, you can take a look at the documentation and customize the above code.
Here is another easy way to restrict the zoom event based on some logic. Because OpenLayers doesnt provide a 'beforezoom'
map.zoomToProxy = map.zoomTo;
map.zoomTo = function (zoom,xy){
// if you want zoom to go through call
map.zoomToProxy(zoom,xy);
//else do nothing and map wont zoom
};
How this works:
For any kind of zooming activity, OpenLayers API ultimately calls the function called zoomTo. So before overriding it, we copy that function to a new function called 'zoomToProxy'. The we override it and add our conditional zoom logic. If we want the zoom to happen we just call new proxy function :)
You could reset the controls array and then add the Zoom and TouchNavigation to it.
var map_controls = [];
map_controls.push( new OpenLayers.Control.Zoom() );
map_controls.push( new OpenLayers.Control.TouchNavigation() );
var map = new OpenLayers.Map({
div: "map",
controls: map_controls
});
Hope it helps ! :)
Related
I followed tutorial from this blog and want to create a tool like TransformationExtension.
I found that the model I selected was not immediately centered on the cursor and there was a gap when dragging it.
document.onmousemove = (event) => {
if (!event.ctrlKey) return;
let res = this.viewer.impl.hitTest(
event.clientX,
event.clientY,
true,
null,
[this.viewer.model.getModelId()]
);
let pt = null;
if (res) {
pt = res.intersectPoint;
} else {
pt = viewer.impl.intersectGround(event.clientX, event.clientY);
}
let tr = this.selectedModel.getPlacementTransform();
tr.elements[12] = pt.x;
tr.elements[13] = pt.y;
tr.elements[14] = pt.z;
this.selectedModel.setPlacementTransform(tr);
this.viewer.impl.invalidate(true, true, true);
};
As #AlexAR mentioned, when finding the cursor position relative to a specific DOM element using the clientX and clientY values, you have to remember subtracting the element's client bounding rect: Find mouse position relative to element.
Btw. I've recently built another sample Forge app that can be used to drag&drop new models into the viewer that you might find helpful: https://github.com/petrbroz/forge-assembly-configurator. The main drag&drop logic is implemented in the setupDragDrop function.
I am working with Google Map for flutter Website. As most tutorial said that, the map is displayed through ui.platformViewRegistry.registerViewFactory
below is the code example. As you see, i create the widget with the variable and tried to give the _lat and _lng from outside. But after i tried to give the value, the ui.platformViewRegistry.registerViewFactory is not triggered.
I just realize that the ui.platformViewRegistry.registerViewFactory objected to create the element so when the element was created, it will not executed again, but I cannot access the element via document.getElementById('map-canvas') either.
Anyone have idea about this?
Widget getMap(double _lat, double _lng) {
String htmlId = "map-canvas";
ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
// final myLatLng = LatLng(-25.363882, 131.044922);
final mapOptions = new MapOptions()
..zoom = 8
..center = new LatLng(_lat, _lng)
..mapTypeControl = false;
final elem = DivElement()
..id = htmlId
..style.width = "100%"
..style.height = "100%"
..style.border = "none";
final map = new GMap(elem, mapOptions);
Marker(MarkerOptions()
..position = LatLng(_lat, _lng)
..map = map
..title = 'Green Energy');
return elem;
});
return HtmlElementView(
viewType: htmlId,
);
}
I found two ways to solve this problem.
It will update the whole view when you change the htmlId and reload.
Either determine how the htmlId should change or give it a new random number with every reload. String htmlId = Random().nextInt(1000).toString();
The disadvantage here is that you always reload the whole map.
This is the better solution as you dont have to reload the complete map. I also had the problem that i could change values inside the ui.platformViewRegistry.registerViewFactory function with data i give to the getMap widget.
So the first part of this solution is to create a global variable that you use inside the ui.platformViewRegistry.registerViewFactory function but update outside.
The second part is to use listener functions inside the ui.platformViewRegistry.registerViewFactory. For example map.onClick.listen((mapsMouseEvent) {}); or map.onZoomChanged.listen((_) {}); or a Stream ANY_STREAM_CONTROLLER.stream.listen((data) {}).
If you would like to add new markers when you zoom out you could do it something like this
ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
map.onZoomChanged.listen((_) {
List<LatLng> marker_list = list_of_markers(map.zoom, map.center);
//Function that gives you a list of markers depending on your zoom level and center
marker_list.forEach((latlng){
Marker marker = Marker(MarkerOptions()
..position = latlng
..map = map
..title = myLatlng.toString()
..label = myLatlng.toString());
});
});
}
I hope the solution helps you
I am using OpenLayers to display dynamically loaded images. The code I am using is:
var map;
function init(strURL) {
map = new OpenLayers.Map('map');
var options = { numZoomLevels: 3,
isBaseLayer: true, };
var graphic = new OpenLayers.Layer.Image(
'City Lights',
strURL + "?sc=page",
new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
new OpenLayers.Size(580, 288),
options
);
// graphic.events.on({
// loadstart: function () {
// OpenLayers.Console.log("loadstart");
// },
// loadend: function () {
// OpenLayers.Console.log("loadend");
// }
// });
var jpl_wms = new OpenLayers.Layer.WMS("NASA Global Mosaic",
"http://t1.hypercube.telascience.org/cgi-bin/landsat7",
{ layers: "landsat7" }, options);
map.addLayers([graphic, jpl_wms]);
map.addControl(new OpenLayers.Control.LayerSwitcher());
map.zoomToMaxExtent();
}
I am calling this function on a button click event and passing the strURL (sources of the images) at that time. The result is, with each click a different image is loaded and displayed on the web page but is not clearing the previous image. So I 5 different images on the webpage are shown with 5 clicks and so on.
My javascript knowledge is limited, so my apologies if this is a stupid question. How to stop this behavior? Thanks for any assistance.
Also, I didn't quite understand the lines:
var jpl_wms = new OpenLayers.Layer.WMS("NASA Global Mosaic",
"http://t1.hypercube.telascience.org/cgi-bin/landsat7",
{ layers: "landsat7" }, options);
But I know these lines are needed since I'm getting js error if I remove them.
So currently you are calling init(strURL) with each button click? Divide that code into two parts:
On page load, create map and layer objects
On button click, just update URL of existing image layer. Image layer has setUrl(url) method for that: http://dev.openlayers.org/apidocs/files/OpenLayers/Layer/Image-js.html#OpenLayers.Layer.Image.setUrl
Here is sample, that should explain it: http://jsfiddle.net/mEHrN/6/
About var jpl_wms = ... - it creates WMS layer, that should display Landsat imagery (but that URL doesn't seem to work). If you remove it, remember also to remove it from map.addLayers:
map.addLayers([graphic]);
Probably this was reason, why you got JS error.
I'm using Google Maps API v3. I would like to create a text overlay on a map that does not move when the map is panned. Is the best approach to manipulate the DOM elements accessible from the MapPanes object or is it best to create a custom control even though it would not do much other than display text?
The simplest way that I found worked for me was a few lines of JavaScript added after I created a new map. So, after this:
map = new google.maps.Map('myMapDivId', mapOptions);
add this:
var myTitle = document.createElement('h1');
myTitle.style.color = 'white';
myTitle.innerHTML = 'Hello World';
var myTextDiv = document.createElement('div');
myTextDiv.appendChild(myTitle);
map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(myTextDiv);
You will probably want to style the text to look nicer.
An alternative is to put the div in your HTML:
<div id="myTextDiv" style="color: white; position: absolute;">
<h1>Hello World</h1>
</div>
and then do this in your JavaScript:
var myControl = document.getElementById('myTextDiv');
map.controls[google.maps.ControlPosition.TOP_CENTER].push(myControl);
NOTE an important difference: If you use the HTML route to define your div, you must set the position style to absolute in the HTML to avoid rendering problems.
From you're describing, the best approach would be a custom control. Docs for that are here. Custom controls can be as simple or a complicated as you want.
One reason why you would want to muck around with the map panes is if you wanted such a 'control' to lie underneath the markers / shadows / polylines etc. I'm doing this right now to show a crosshairs in the center of the map at all times. But because I keep it as an overlay, I choose the panes in such a way that the markers are above it, so they can continue to be clicked and interacted with - using the mapPane. Here's how I'm doing it:
var CrosshairOverlay = function(map){
this._holder = null;
this.setMap(map);
};
CrosshairOverlay.prototype = new google.maps.OverlayView();
CrosshairOverlay.prototype.onAdd = function(){
var map = this.getMap();
var holder = this._holder = $('<div>').attr('id', 'crosshair')[0];
var crosshairPaper = this._paper = R(holder, 150, 150);
// ... all your drawing and rendering code here.
var projection = this.getProjection();
var wrappedHolder = $(holder);
var updateCrosshairPosition = function(){
var center = projection.fromLatLngToDivPixel(map.getCenter());
wrappedHolder.css({left:center.x-75, top:center.y-75});
}
_.each(['drag','dragend','bounds_changed','center_changed','zoom_changed','idle','resize'], function(event){
google.maps.event.addListener(map, event, updateCrosshairPosition);
});
google.maps.event.addListener(map, 'maptypeid_changed', function(){
_.defer(updateCrosshairPosition);
});
this.getPanes().mapPane.appendChild(holder);
};
CrosshairOverlay.prototype.draw = function(){
};
CrosshairOverlay.prototype.onRemove = function(){
this._holder.parentNode.removeChild(this._holder);
this._holder = null;
};
The reason the maptypeid_changed has its own handler with a defer is because that event is fired before the map properly sets itself up when changing the type. Just run your function after the current event loop.
I cannot seem to figure out why the object returned by getProjection() is undefined. Here is my code:
// Handles the completion of the rectangle
var ne = recBounds.getNorthEast();
var sw = recBounds.getSouthWest();
$("#map_tools_selat").attr( 'value', sw.lat() );
$("#map_tools_nwlat").attr( 'value', ne.lat() );
$("#map_tools_selng").attr( 'value', ne.lng() );
$("#map_tools_nwlng").attr( 'value', sw.lng() );
// Set Zoom Level
$("#map_tools_zoomlevel").attr( 'value', HAR.map.getZoom()+1 );
document.getElementById("map_tools_centerLat").value = HAR.map.getCenter().lat();
document.getElementById("map_tools_centerLong").value = HAR.map.getCenter().lng();
// All this junk below is for getting pixel coordinates for a lat/lng =/
MyOverlay.prototype = new google.maps.OverlayView();
MyOverlay.prototype.onAdd = function() { }
MyOverlay.prototype.onRemove = function() { }
MyOverlay.prototype.draw = function() { }
function MyOverlay(map) { this.setMap(map); }
var overlay = new MyOverlay(HAR.map);
var projection = overlay.getProjection();
// END - all the junk
var p = projection.fromLatLngToContainerPixel(recBounds.getCenter());
alert(p.x+", "+p.y);
My error is: Cannot call method 'fromLatLngToContainerPixel' of undefined
Actually, i the reason why this happens is because the projection object is created after the map is idle after panning / zooming. So, a better solution is to listen on the idle event of the google.maps.Map object, and get a reference to the projection there:
// Create your map and overlay
var map;
MyOverlay.prototype = new google.maps.OverlayView();
MyOverlay.prototype.onAdd = function() { }
MyOverlay.prototype.onRemove = function() { }
MyOverlay.prototype.draw = function() { }
function MyOverlay(map) { this.setMap(map); }
var overlay = new MyOverlay(map);
var projection;
// Wait for idle map
google.maps.event.addListener(map, 'idle', function() {
// Get projection
projection = overlay.getProjection();
})
I kind of figured out what was going on. Even though it is still not crystal clear why this happens, I know that I had to instantiate the variable "overlay" right after instantiating my google map (HAR.map). So I practically moved that code snippet into my HAR class and now i use:
HAR.canvassOverlay.getProjection().fromLatLngToContainerPixel( recBounds.getCenter() );
So now, every time I create a map via my class "HAR" I also have a parallel OverlayView object within my class.
The Error could have been with losing scope of my class object, but I think it was more of the map event "projection_changed" not being fired. I got a hint from the map API docs for map class, under method getProjection():
"Returns the current Projection. If the map is not yet initialized (i.e. the mapType is still null) then the result is null. Listen to projection_changed and check its value to ensure it is not null."
If you are getting the similar issue, make sure that you assign your overlayView.setMAP( YOUR_MAP_OBJECT ) closely after instantiating the map object.