Restrict open layers map dragging out of screen - javascript

I want my open layers map to fit the full screen. I want users to be able to navigate inside, zoom, and drag it.
Here's a fiddle to work on : http://jsfiddle.net/mhicauber/t8K4p/1/
My problème is that I don't understand some values I'm giving at map creation :
mapLayer = new OpenLayers.Layer.Image(
'My map',
'http://tchanca.com/private/Masse1080.jpg',
OpenLayers.Bounds.fromString("-160,-90.0,160,90.0"), new OpenLayers.Size(screenSize.width, screenSize.height), {
maxExtent: OpenLayers.Bounds.fromString("-160,-90.0,160,90.0")
});
With those values, user is able to drag a part of the map out of screen. If I change the maxExtent values to 0,0,0,0, well, map is contained in the screen and can't be dragged outside but as soon as I zoom in, I'm unable to drag the map.
What are the -160,-90.0,160,90.0 values refering to please ? What code should I put to :
make the map fit the screen at loading
allow user to zoom and drag it
but restrict dragging to screen size ?
Thank you very much. And please excuse my lack of knowledge on cartography, I'm pretty noob on the subject...

For those of you who come here and are using OpenLayers 3, here is some information that might be useful.
There is no more restrictedExtent. Instead you have to set the 'extent' option in your layers and view.
There is also no more OpenLayers.Bounds. Instead you use ol.extent, which is a an array with 4 values.
Example:
var maxExtent = ol.proj.transformExtent([-122.445717,47.576989,-122.218094,47.71623], 'EPSG:4326', 'EPSG:3857')
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.Stamen({
layer: 'watercolor'
}),
extent: maxExtent
}),
new ol.layer.Tile({
source: new ol.source.Stamen({
layer: 'terrain-labels'
}),
extent: maxExtent
})
],
target: 'map',
view: new ol.View({
center: ol.proj.transform(
[-122.333407, 47.607436], 'EPSG:4326', 'EPSG:3857'),
extent: maxExtent,
zoom: 14
})
});

I think the property you're actually looking for is restrictedExtent. The restrictedExtent controls the portion of the map the user is able to pan around. If you don't restrict the zoom level then the user will still be able to zoom out to view the rest of the map, but they won't be able to pan the map.
Firstly, rather than using OpenLayers.Bound.FromString("-160,-90.0,160,90.0"), you should probably use new OpenLayers.Bounds(-160, -90, 160, 90).
In the case of (-160, -90, 160, 90) (possibly the whole map?), this means the user is free to pan around those coordinates, if this is the whole map then it's not restricting anything.
In the case of restricting the extent to (0, 0, 0, 0) - the centre of the map, the user is not able to pan anywhere - the map is always fixed over the center spot - but they may still zoom freely because that is not controlled by restrictedExtent.
The values in (-160, -90, 160, 90") are coordinates in degrees - it's a bounding box (left, bottom, right, top). There are all sort of different coordinate systems that can be used, you can explicitly specify which coordinate system you're going to use with projection: "EPSG:3857".
In the case of a custom map layer like yours, they're just coordinates in relation to the bounds you pass in when creating the layer.
To solve your problem:
I create the map layer with the bounds new OpenLayers.Bounds(-180,-90.0,180,90.0) and set a restrictedExtent on the map (rather than the layer) with the same bounding box.
map = new OpenLayers.Map('map', {
controls: [],
restrictedExtent: new OpenLayers.Bounds(-180, -90, 180, 90)
});
mapLayer = new OpenLayers.Layer.Image(
'My map',
'http://tchanca.com/private/Masse1080.jpg',
new OpenLayers.Bounds(-180,-90.0,180,90.0),
new OpenLayers.Size(screenSize.width, screenSize.height),
{}
);
map.addLayer(mapLayer);
Since your mapLayer occupies the full extent of the map the user can't zoom out beyond it to see any white margin, and also can't pan outside it since nothing exists beyond it.
JSFiddle

Here is another approach for OpenLayers3. Depending on what you want to refer to the easiest way is to get the extent directly from your desired projection (ol.proj.Projection) or layer (any class of ol.layer) using the getExtent() function. In case of EPSG:3857 this works:
var map = new ol.Map({
...
view: new ol.View({
...
extent: ol.proj.get("EPSG:3857").getExtent()
})
});

I created a very basic answer here at this gist: https://gist.github.com/cfh294/e4495b9ff6d989db950ccd2573422808#file-restrictedextent-js
The "meat and potatoes":
map.on("movestart", function(evt) {
panStartCenter = view.getCenter();
});
map.on("moveend", function(evt) {
var panEndCenter = view.getCenter();
var x = panEndCenter[0];
var y = panEndCenter[1];
});
// if the center no longer resides in the max extent, snap the map back to where it was
// before the pan
if (!(ol.extent.containsXY(maxExtent, x, y))) {
view.setCenter(ol.Coordinate(panStartCenter));
}
});

Related

Azure maps cluster position discrepancies

I've got a list of positions that I retrieve from an ms endpoint like so:
https://atlas.microsoft.com/search/fuzzy/json?top=100&typeahead=true&subscription-key=subscription-key&api-version=1&query=Leeds
The user then selects one of the proposed addresses and the position provided by this endpoint is then used to be displayed on a map that uses clusters. So for Leeds for example, I have the following:
-1.548567, 53.801277
However, when I create the clusters in the clusterRenderCallback function I provide upon creating the HtmlMarkerLayer, I get positions that are close to the ones I've provided, but different and I have no idea how or why.
so the code would look something like:
First I create the dataSource
dataSource = new atlas.source.DataSource(null, {
//Tell the data source to cluster point data.
cluster: true
});
map.sources.add(dataSource);
Then I manage the cluster creation inside the HtmlMarkerLayer creation:
clusterRenderCallback: function (id, position, properties) {
var cluster = new atlas.HtmlMarker({
position: position, // different position to that which I have provided
htmlContent: `<div>${properties.point_count_abbreviated}</div>`,
values: properties,
});
map.events.add('click', cluster, clusterClicked);
return cluster;
}
And here I create points to add to my data source:
let features = list.map(x => new atlas.data.Feature(new atlas.data.Point(new atlas.data.Position(x.lon, x.lat)), x));
dataSource.add(features);
The position I receive for the Leeds cluster for example is -1.549072265625, 53.80065082633024, even though I had 2 poisitions in Leeds, both made out of the same coordinates: -1.548567, 53.801277
It seems like there's some sort of mechanism inside the atlas code that "fixes" the provided coordinates; anyone knows how to stop this or what am I doing wrong here?
==EDIT 02/05==
ok, so following #rbrundritt answer, here the last bit of code I should have added, showing what we do once the cluster is clicked:
function clusterClicked(e) {
var cluster = e.target;
datasource.getClusterExpansionZoom(cluster.properties.cluster_id).then(function (zoom) {
map.setCamera({
center: cluster.getOptions().position,
zoom: zoom
});
});
}
And this is where we have our problem with this discrepancy- clicking on the cluster zooms in to the zoom level where the cluster breaks; however, since we centre the map to the cluster position, the pin position, being different to the cluster one, is not being seen in the map on that zoom level (ie 3 afair). On top of that we have no way of knowing inside the context of this function to what pin does the cluster corresponds, this leaves us with a buggy behaviour.
Clusters should rarely have the same coordinates as any points that they contain. Clusters group overlapping points together and an average position is used to represent that group on the map. When a cluster breaks apart into its individual points then that marker will have the original position value.
Ok, so I wasn't aware of what the datasource.getClusterLeaves method was returning (I've simply mistaken the leaves for the verb leaving). This is what I was looking for, so my code now looks like this:
function inSamePosition(pos1, pos2) {
return pos1.data.geometry.coordinates[0] == pos2.data.geometry.coordinates[0]
&& pos1.data.geometry.coordinates[1] == pos2.data.geometry.coordinates[1];
}
function clusterClicked(e) {
var cluster = e.target;
(cluster.properties.cluster_id, Number.POSITIVE_INFINITY, 0).then(pins => {
let position = pins.every(p => inSamePosition(p, pins[0])) ? pins[0]['data'].geometry.coordinates : null;
datasource.getClusterExpansionZoom(cluster.properties.cluster_id).then(function (zoom) {
map.setCamera({
center: position ? position : cluster.getOptions().position,
zoom: zoom
});
});
})
}

Openlayers 3. Clusters

I use Openlayers 3. I have a layer with clustering:
var layer = new ol.layer.Vector({
source: new ol.source.Cluster({
distance: 10,
source: new ol.source.Vector({
features: []
})
})
});
I add two features to it this way:
this.feature1 = <some feature with style>;
this.feature2 = <some feature with style>;
layer.getSource().getSource().addFeature(this.feature1);
layer.getSource().getSource().addFeature(this.feature2);
Then I want to hide my features by setting theire styles to null:
this.feature1.setStyle(null);
this.feature2.setStyle(null);
So, on the map these features are not visible. But if these features are placed close to each other, the cluster marker is shown. So, my question is the next: is any way not to consider features with empty style in clustering?
You can specify a "geometryFunction" parameter whey you create your Cluster source.
This function will get called and give you the ability to determine what should be included in the cluster.

Infobox position always towards centre of map in Bing Maps

I am using Bing Maps v8 and I would like to get the Infobox to be a little smart on where it shows.
Here is a CodePen of what I have. If you hover over the pushpin, you'll notice that the infobox shows towards the top and is thus cut off from view.
function loadMapScenario() {
var map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
credentials: 'Your Key Here',
center: new Microsoft.Maps.Location(51.50632, -0.12714)
});
var pinLocation = new Microsoft.Maps.Location(51.663088, -0.204133);
var pushpin = new Microsoft.Maps.Pushpin(pinLocation, null);
var infobox = new Microsoft.Maps.Infobox(pinLocation, { title: 'Map Center', description: 'London, UK', visible: false });
infobox.setMap(map);
Microsoft.Maps.Events.addHandler(pushpin, 'mouseover', function () { infobox.setOptions({ visible: true }) });
map.entities.push(pushpin);
}
Is there a way to make that smart, so that it always shows towards the centre of the map, this way it'll always be in view.
I have found this which moves the map about so that the infobox is within view, but that is not what I'm after. I don't want the map moving about, just the infobox to show towards the centre of the map.
There is a custom infobox module available that provides this exact functionality. It will reposition itself so that it is always in view. You can find this module here: http://bingmapsv7modules.codeplex.com/wikipage?title=Custom%20Infobox%20Control
It is supported in Bing Maps V7 and V8.

Initializing two leaflet maps - one rewrites another

I'm trying to put multiple leaflet maps on one page, but I have problem with two of them - the second one rewrites the first one, so first one is rendering map, and the second has just grey area.
Here is the code:
var map_a = new L.Map( "smallMap", {
fullscreenControl: false,
layers: [osmMap]
}).setView([51.005, -0.09], 14);
var map_b = new L.Map( "smallMapSecond", {
fullscreenControl: false,
layers: [osmMap]
}).setView([51.505, -0.09], 14);
I have appropriate divs called smallMap and smallMapSecond. When I Initialize only one map, it works. Where could be the problem?
Well,one of the potential problems I see here without having a jsFiddle available is that you are using the same tileLayer osmMap for both maps. Sharing TileLayers between map instances is going to cause problems. Initialize tileLayers one per map.

How to reproject a vector layer when you switch between base maps of different projections

I have OpenLayers map with two base layers: MetaCarta (EPSG:4326) and Google map (Mercator). sphericalMercator = false, units are degrees. There are also some markers, boxes, and vector data on the map.
When I switch between the base layers (which are of different projections), the simple geometries (such as markers or boxes) are reprojecting automatically and displayed correctly. However vector layers (polylines) are just shifted, not reprojected. I think that I need to call some kind of "rebuild" function or add some parameter so that OpenLayers do this automatically when the base layer projection changes. But I have no idea how to do this.
I read about Spherical Mercator (http://docs.openlayers.org/library/spherical_mercator.html) and look through OpenLayers examples, but didn't find a solution.
The part of my code is below (all coordinates in vector.json is in degrees):
var metaCarta = new OpenLayers.Layer.WMS("MetaCarta",
"http://labs.metacarta.com/wms/vmap0?",
{layers: "basic"}
);
var gmap = new OpenLayers.Layer.Google(
"Google Streets",
{numZoomLevels: 40}
);
map.addLayers([metaCarta, gmap]);
map.setCenter(new OpenLayers.LonLat(139.8, 35.7), 11);
// Load vector data
var jsonFormat = new OpenLayers.Format.GeoJSON();
var vectorLayer = new OpenLayers.Layer.Vector("vector", {
style: {strokeColor: "gray",strokeWidth: 2}
});
OpenLayers.loadURL("vector.json", {}, null, function(response) {
var features = jsonFormat.read(response.responseText);
vectorLayer.addFeatures(features);
});
map.addLayer(vectorLayer);
You will need to define the projections and a suitable transform in OpenLayers. In turn, you will need to include the Proj4JS library (which is used by OpenLayers to perform these projection transformations)

Categories

Resources