currently i have a vector map displayed using a KML file as the vector source.
what i want to do is have an image underlay this vector map.
The map is an indoor floormap, the image is the exact same as the vector map only there is more details in it, text written on it etc. What i need is for the map image to underlay the vector map so that the walls of the vector map align perfectly with the walls of the image. This can happen because the KML was created by tracing on top of the images using QGIS.
so far ive been able to have the KML vector map and png image appear on the map, but they are not alligned with each other and are not the same size. This is what i need help with!
here some code for what i currently have:
create the map, no layers yet (maps selected from dropdown boxes)
var map = new ol.Map({
layers: [],
target: 'floormap',
interactions: ol.interaction.defaults({mouseWheelZoom:false}),
view: new ol.View({
center: [0, 0],
zoom: 19,
minZoom: 15,
maxZoom: 30
})
});
add selected map (KML) to map
map.removeLayer(vector);
vector = new ol.layer.Vector({
source: new ol.source.Vector({
url: MAPS_URL + maps[map_id],
format: new ol.format.KML()
})
});
map.addLayer(vector);
setMapExtent(vector);
now i tried to add the image, which works but its not alligned
// this part here i feel may be the problem,
// i just copied and pasted from an example om openlayers.org,
// i dont actually know much about the extent and how to match it to
// the vector map
var extent = [0,0,1024,684];
var projection = new ol.proj.Projection({
code: 'xkcd-image',
units: 'pixels',
extent: extent
});
image = new ol.layer.Image({
source: new ol.source.ImageStatic({
attributions: [
new ol.Attribution({
html: '© xkcd'
})
],
url: MAPS_URL + images[map_id],
projection: projection,
imageExtent: extent
})
});
map.addLayer(image);
the setMapExtent method
function setMapExtent(vectorMap) {
var vectorSource = vectorMap.getSource();
var listenerKey = vectorSource.on('change', function () {
if (vectorSource.getState() === 'ready') {
var extent = vectorSource.getExtent();
map.getView().fitExtent(extent, map.getSize());
vectorSource.unByKey(listenerKey);
}
});
}
at this point i have a vector map with an image sitting way above the map, and the image seems to be smaller too.
Can anyone help me with this issue?
*** Solution! ***
a working solution, although probably not the best way to do it, but it works none the less.
var map = new ol.Map({
layers: [],
target: 'floormap',
interactions: ol.interaction.defaults({mouseWheelZoom:false}),
view: new ol.View({
center: [0, 0],
zoom: 19,
minZoom: 15,
maxZoom: 30
})
});
add new map layer
map.removeLayer(vector);
vector = new ol.layer.Vector({
source: new ol.source.Vector({
url: MAPS_URL + maps[map_id],
format: new ol.format.KML()
})
});
map.addLayer(vector);
setMapExtent(vector);
// call image adding function pass in vector
// to get its extend
addImage(vector);
the addImage function
function addImage(vectorMap) {
var vectorSource = vectorMap.getSource();
// listen for one change on the vector to get the extent of it
// for use in setting the image extent. tried to use on.('load')
// but it didnt work
var listenerKey = vectorSource.once('change', function () {
var extent = vectorSource.getExtent();
var projection = new ol.proj.Projection({
code: 'xkcd-image',
units: 'pixels',
extent: extent
});
image = new ol.layer.Image({
source: new ol.source.ImageStatic({
attributions: [],
url: MAPS_URL + images[map_id],
projection: projection,
imageExtent: extent
})
});
// remove vector layer else they keep stacking up
map.removeLayer(vector);
// add image
map.addLayer(image);
// re-add vector only push so it goes above the image
map.getLayers().push(vector);
});
}
seems to work pretty well! can anyone help me with layer ordering?
Your static image must be georeferenced correctly with the view's projection.
Default view's projection is EPSG:3857 (Spherical Mercator)., the extent of this projection is [-20026376.39, -20048966.10, 20026376.39, 20048966.10]
In your code you specify a projection in pixels for your static layer. You need to use the view's projection, something like this :
// Here the extent of your layer in EPSG:3857 -> [minx, miy, max, mayy]
var extent = [-10000000, -10000000, 10000000, 10000000];
image = new ol.layer.Image({
source: new ol.source.ImageStatic({
attributions: [
new ol.Attribution({
html: '© xkcd'
})
],
url: MAPS_URL + images[map_id],
imageSize: [1024, 684], // Don't forget the image size here
imageExtent: extent
})
});
map.addLayer(image);
Update:
For layer ordering if you want your vector layer on top use push:
http://openlayers.org/en/v3.8.2/apidoc/ol.Collection.html#push
map.getLayers().push(vector)
Related
I used OpenLayers in Angular2 to show a custom SVG-image as a background map. This works perfectly and I can zoom and scroll across my map.
Now I implemented Features (also svg) and put them all in a seperate Vector-Layer above the map. They are all shown but when I zoom, they get resized weird.
When I zoom out, the Features are getting bigger, when I zoom in the features are getting smaller. I would like to have them always in the same size, so that they are getting smaller when I zoom out (as if it would be static content on the map).
I tried with an extra style-function and experimented by recalculation the scale of the current style of the feature. But I did not get this to work.
So my question ist: How do I put another Layer with Features above an existing layer and when zooming out everything gets smaller?
Am I doing something wrong? Do I need Projections? I am quite new to Openlayers.
Thanks in advance!
A minimal code example, the SVG-Points are getting bigger and bigger when zooming out (same problem at the OL code example: Official Code Example: When Zooming out, the marker gets as big as a whole country or continent....
let center = ol.proj.transform([8.30368, 47.05243], 'EPSG:4326', 'EPSG:3857');
let style = new ol.style.Style({
image: new ol.style.Icon({
anchor: [0, 0],
anchorXUnits: 'pixels',
anchorYUnits: 'pixels',
src: '/assets/images/test.svg',
imgSize: [50, 50],
size: [50, 50],
scale: 1.0
})
});
let iconFeature = new ol.Feature({
geometry: new ol.geom.Point(center)
});
iconFeature.setStyle((resolution) => {
return [iconStyle];
});
this.featureArray.push(iconFeature);
let vectorSource = new ol.source.Vector({
features: this.featureArray
});
let extent = [0, 0, 1024, 968];
let projection = new ol.proj.Projection({
code: 'xkcd-image',
units: 'pixels',
extent: extent
});
// load vector layer for dynamic elements
let vectorLayer = new ol.layer.Vector({
source: vectorSource,
projection: projection,
imageExtent: extent
});
this.layerArray.push(vectorLayer);
// basic setup
this.map = new ol.Map({
renderer: 'canvas',
layers: this.layerArray,
view: new ol.View({
center: center,
zoom: 16,
minZoom: 1,
maxZoom: 26,
})
});
You mean something like https://codepen.io/anon/pen/WOmqmN?&editors=1110?
That requires a style function as style, where you scale the icon according to the resolution:
function(feature, resolution) {
style.getImage().setScale(16000 / resolution);
}
16000 ist the resolution at which the icon size should be the icon image's original size.
Also note that the ol.layer.Vector instance is configured with
updateWhileAnimating: true,
updateWhileInteracting: true
to make sure the icon size is updated whenever the resolution changes.
Asked at GIS, but nothing forthcoming, hoping someone here might have ideas. Sorry for the cross post. Am updating a project from OL2, where what I'm attempting worked fine. Project isn't a map, it's images of text documents converted to zoom tiles.
Added a vector layer of polygons that represent the coordinates of terms/phrases in text found in the OCR text.
This fiddle shows a page with highlighting for 3 hits on the term "texas". Note the position of the terms is correct on one axis, but simply too high on the screen. Again, same pixel coords worked in OL2.
Excerpt from fiddle:
var vectorSource = new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(geojsonObject)
});
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: source
}),
new ol.layer.Vector({
source: vectorSource,
style: styleFunction
})
],
target: 'zoom',
view: new ol.View({
projection: proj,
constrainRotation: 0,
center: imgCenter,
zoom: 0,
// constrain the center: center cannot be set outside
// this extent
extent: [0, -imgHeight, imgWidth, 0]
})
});
Problem seems to be related to the projection, extent, or centering. Any help here would be greatly appreciated.
It seems you're using wrong coordinates. I made a fiddle which you can draw a box around a text and you'll see the coordinates on top.
So if you change for example your coordinates to:
var geojsonObject = {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'geometry': {
'type': 'MultiPolygon',
'coordinates': [
[[[1849.90, -2385.40], [1849.90, -2619.07], [2258.83, -2619.07], [2258.83, -2385.40]]]
]
}
}
]
};
The polygon will be where you want.
I am working with the application Beta_Here which uses leaflet plugins, all libraries are local except for few(css related)
Usage of application live
First View:This application get input from user and set the distance
calculation formula accordingly....
Second View : After entering input e.g 9, second view will be loaded
where we can draw shapes....
Introduction
I have setup the script which will load two imageoverlays(layers) and
we can toggle them from top right and we can draw or measure from
bottom left....
Problem
When we draw shapes or put markers on an image, controls work nearly
perfect but when we toggle the layers, there starts the problem....
all shapes go to the background or (it seems they disappeared)
Main Question
How can we bind the drawings and marker to the specific
layer(imageoverlay) if there is a way as we can see the drawing are
not bind with the images but the map container..... (Pardon me if you
feel i am doing something stupid because i have limited knowledge
about layers so i came up with my question here....
If someone has idea about how to solve this problem, please do help or
any kind of reference will be appreciated... Thanks for your time
Working Script
var map = L.map('map', {
minZoom: 1,
maxZoom: 4,
center: [0, 0],
zoom: 0,
crs: L.CRS.Simple
});
// dimensions of the image
var w = 3200,
h = 1900,
mainurl = 'assets/img/isbimg.jpg';
childurl = 'assets/img/fjmap.png';
// calculate the edges of the image, in coordinate space
var southWest = map.unproject([0, h], map.getMaxZoom() - 1);
var northEast = map.unproject([w, 0], map.getMaxZoom() - 1);
var bounds = new L.LatLngBounds(southWest, northEast);
var featureGroup = L.featureGroup().addTo(map);
var drawControl = new L.Control.Draw({
edit: {
featureGroup: featureGroup
},
draw: {
polygon: true,
polyline: true,
rectangle: true,
circle: true,
marker: true
}
}).addTo(map);
map.on('draw:created', showPolygonArea);
map.on('draw:edited', showPolygonAreaEdited);
// add the image overlay,so that it covers the entire map
L.control.layers({
Main: L.imageOverlay(mainurl, bounds),
Child: L.imageOverlay(childurl, bounds)
}, null, { collapsed: false }).addTo(map);
L.control.nanomeasure({ nanometersPerPixel: 10000 }).addTo(map);
// tell leaflet that the map is exactly as big as the image
map.setMaxBounds(bounds);
L.tileLayer({
attribution: 'SmartMinds',
maxZoom: 18
}).addTo(map);
//polygon area customization
function showPolygonAreaEdited(e) {
e.layers.eachLayer(function (layer) {
showPolygonArea({ layer: layer });
});
}
function showPolygonArea(e) {
var userInputCustom = prompt("Please enter image name : choose between a to f");
featureGroup.addLayer(e.layer);
e.layer.bindPopup("<div style='width:200px;height:200px;background-image: url(assets/img/" + userInputCustom + ".png);background-size: 195px 195px;;background-repeat: no-repeat;'></div>");
e.layer.openPopup();
}
});
I would contain those FeatureGroup and ImageOverlay pairs into L.LayerGroup's. Then you can switch between those groups. Then you can keep track of the currently selected group, and add your features to the featurelayer of that group. I can explain it better with code through comments:
Basic map, nothing special:
var map = L.map('map', {
'center': [0, 0],
'zoom': 1,
'layers': [
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
'attribution': 'Map data © OpenStreetMap contributors'
})
]
});
// Bounds for the map and imageoverlays
var bounds = L.latLngBounds([[40.712216, -74.22655],[40.773941, -74.12544]]);
// Set bounds on the map
map.fitBounds(bounds);
The grouping part:
// New layergroup, note it's not added to the map yet
var layerGroup = new L.LayerGroup(),
imageOverlayUrl = 'https://placeholdit.imgix.net/~text?txtsize=33&txt=Overlay 1&w=294&h=238',
// New imageoverlay added to the layergroup
imageOverlay = new L.ImageOverlay(imageOverlayUrl, bounds).addTo(layerGroup),
// New featuregroup added to the layergroup
featureGroup = new L.FeatureGroup().addTo(layerGroup);
// Second layergroup not added to the map yet
var layerGroup2 = new L.LayerGroup(),
imageOverlayUrl2 = 'https://placeholdit.imgix.net/~text?txtsize=33&txt=Overlay 2&w=294&h=238',
// New imageoverlay added to the second layergroup
imageOverlay2 = new L.imageOverlay(imageOverlayUrl2, bounds).addTo(layerGroup2),
// New featuregroup added to the second layergroup
featureGroup2 = new L.FeatureGroup().addTo(layerGroup2);
Default drawcontrol and layercontrol with both layergroups added as baselayers:
var layerControl = new L.control.layers({
'Group 1': layerGroup,
'Group 2': layerGroup2
}).addTo(map);
var drawControl = new L.Control.Draw().addTo(map);
Here's where the magic happens ;) :
// Variable to hold the selected layergroup's featuregroup.
var currentFeatureGroup;
// Catch the layer change event
map.on('baselayerchange', function (layersControlEvent) {
// Loop over the layers contained in the current group
layersControlEvent.layer.eachLayer(function (layer) {
// If it's the imageoverlay make sure it's in the background
if (layer instanceof L.ImageOverlay) {
layer.bringToBack();
// If not then it's the featuregroup, reference with variable.
} else {
currentFeatureGroup = layer;
}
});
});
// Catch draw created event
map.on('draw:created', function (e) {
// Store created feature into the current featuregroup
currentFeatureGroup.addLayer(e.layer);
});
That's it. Pretty basic just meant as an example but it does what you want it to do. A real implementation would look different, with errorhandling because for instance when you draw and have no baselayer/overlay selected it fail etc. Here's a working example on Plunker to play with: http://plnkr.co/edit/6cGceX?p=preview
I am developing a solution that uses Openlayers 3 to display static images of text documents that have been converted from PDFs. Some of my documents are black text on a white background - the page I'm displaying openlayers on is also black, as is the map background - so the document is invisible.
My current solution is to draw a white filled polygon at the same extent as the image being loaded but behind the text - essentially providing a white background for the extent I'm interested in.
The problem is when I pan around, the polygon is constantly redrawing, causing some undesirable effects. See this jsFiddle for an example.
Is there an alternative way of setting the background colour of a layer or is there a setting that will prevent the polygon from flickering.
CSS
body{
background-color:black;
}
HTML
<div id="map" class="map"></div>
Javascript
var imgURL = 'http://i.stack.imgur.com/9C8Xu.png';
var extent = [0,0,488,198];
var projection = new ol.proj.Projection({
units: 'pixels',
extent: extent
});
//A polygon that will represent a white background
var polyFeature = new ol.Feature({
geometry: new ol.geom.Polygon([
[
[0, extent[1]],
[0, extent[3]],
[extent[2], extent[3]],
[extent[2], extent[1]]
]
])
});
//A base layer to hold the polygon feature
var baseLayer = new ol.layer.Image({
source: new ol.source.ImageVector({
source: new ol.source.Vector({
features: [polyFeature]
}),
style: new ol.style.Style({
fill: new ol.style.Fill({color: 'white'})
})
})
});
var docLayer = new ol.layer.Image({
source: new ol.source.ImageStatic({
url: imgURL,
imageExtent: extent
})});
// build the map
var map = new ol.Map({
layers:[baseLayer,docLayer],
target: 'map',
view: new ol.View({
projection: projection,
center: ol.extent.getCenter(extent),
zoom: 2.5
})
});
I'm trying to overlay an image over an OpenLayers 3.0 map by adding a point feature to the layer, and setting the icon to the image to load. How can I get it to scale with the map as it is being zoomed?
Or is there a better way to overlay an image atop a layer?
p=ol.proj.transform( [-78,40],'EPSG:4326','EPSG:3857')
var f=new ol.Feature({ geometry: new ol.geom.Point(p) });
var imgStyle=new ol.style.Style({
image: new ol.style.Icon(({
rotateWithView: false,
anchor: [.5,.5],
anchorXUnits: 'fraction', anchorYUnits: 'fraction',
opacity: 0.75,
src: 'http://www.viseyes.org/shiva/map.jpg'
}))
});
f.setStyle(imgStyle);
myLayerr.getSource().addFeature(f);
It looks like you are trying to use an image of a map as an overlay. Instead of using the image as an icon for a feature with a point geometry, you'd be better off using an image layer with a static image source. See the code below for an example (also http://jsfiddle.net/tschaub/orr6qfkc/).
var extent = ol.proj.transformExtent(
[-5.6342, 50.3331, 1.6607, 53.0559], 'EPSG:4326', 'EPSG:3857');
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.MapQuest({layer: 'osm'})
}),
new ol.layer.Image({
source: new ol.source.ImageStatic({
url: 'http://www.viseyes.org/shiva/map.jpg',
imageExtent: extent
})
})
],
view: new ol.View({
center: ol.extent.getCenter(extent),
zoom: 7
})
});
I've just guessed at the geographic extent of the image. It may also be that the image overlays better on a map with the view projection set to 'EPSG:4326'.
Note that if you want to use an icon to symbolize a point feature and you want it to rotate and scale with the map (as the title of this question implies), you need to do two things:
Set the rotateWithView option to true (the default of false means that the icon will not rotate when you rotate the map).
Give your vector layer a style function. This function will be called with your feature and the view resolution. You can then scale your icon using the resolution. The style function below should give you a rough idea of how this could work.
// resolution at which to display the
// icon at 1:1
var maxResolution = 10000;
function style(feature, resolution) {
var icon = new ol.style.Style({
image: new ol.style.Icon({
src: 'http://example.com/icon.png',
scale: maxResolution / resolution,
rotateWithView: true
}))
});
return [symbolizer];
}
Thanks to Tim's help, I was able to rotate image markers that scale with the view. Max
Here's the final code :
var p=ol.proj.transform( [-78,40],'EPSG:4326','EPSG:3857')
var f=new ol.Feature({ geometry: new ol.geom.Point(p) });
this.boxLayer.getSource().addFeature(f);
this.boxLayer.setStyle(function(feature, resolution) {
var styleArray = [ new ol.style.Style( {
image: new ol.style.Icon({
src: 'http://www.viseyes.org/shiva/map.jpg',
scale: maxResolution/resolution,
rotateWithView: true,
rotation: .5,
anchor: [.5,.5],
anchorXUnits: 'fraction', anchorYUnits: 'fraction',
opacity: 0.75
})
})];
return styleArray;
});