I have a problem. I have a short list of clients and for each of them I want to display a pin and Popup window, which will be displayed after clicking the info window. However, I have no idea how to connect it.
Part of my code:
List<Client> lstClients = new List<Client>
{
new Client(1, "Firma 1", "Wspólna 10", "123-123-23-23", "F1", true),
new Client(2, "Firma 2", "Marszałkowska", "456-456-56-45", "F2", false),
new Client(3, "Firma 3", "Jerozolimskie 57", "789-789-89-78", "F3", true),
new Client(4, "Firma 4", "Koszykowa 10", "234-423-43-23", "F4", false)
};
foreach (Client client in lstClients)
{
var geoadres = client.Address;
var locations = await Geocoding.GetLocationsAsync(client.Address);
var location = locations?.FirstOrDefault();
ListPin = new Pin
{
Type = PinType.Place,
Label = client.FirmName,
Address = client.Address,
Position = (new Position(location.Latitude, location.Longitude)),
Rotation = 33.3f,
Tag = client.Tag
};
map.Pins.Add(ListPin);
}
void InfoWindow_Clicked(object sender, InfoWindowClickedEventArgs e)
{
PopupNavigation.Instance.PushAsync(new ShowPopup());
}
I will be grateful for any help.
By clicking on the pin need to show the pop up window, you can implement like this;
foreach (Client client in lstClients)
{
var geoadres = client.Address;
var locations = await Geocoding.GetLocationsAsync(client.Address);
var location = locations?.FirstOrDefault();
ListPin = new Pin
{
Type = PinType.Place,
Label = client.FirmName,
Address = client.Address,
Position = (new Position(location.Latitude, location.Longitude)),
Rotation = 33.3f,
Tag = client.Tag
};
map.Pins.Add(ListPin);
// tap event from map pinview -> Callout action
map.Pins[i].Clicked += (sender, e) =>
{
System.Diagnostics.Debug.WriteLine(((Pin)sender).Type);
DisplayAlert(((Pin)sender).Label, ((Pin)sender).Address, "OK");
};
}
add the code inside for-loop
I'm trying to add a satellite image on my map using OpenLayers 5.
The problem is that I'm not able to do this, because I've just found an option to add an image on the map passing the image extent (xmin, ymin, xmax, ymax) and not the bounding box. The image should fit inside the bounding box. For that reason, the image was distorted.
The image is in JPG file (attribute feature.properties.icon). Example: http://exampleserver.com/220/063/353LGN00/353LGN00_thumb_large.jpg
The result that I would like is something like this:
The result that I've got was that:
My code that adds this image on the map is the following:
import ImageLayer from 'ol/layer/Image'
import Static from 'ol/source/ImageStatic'
...
this.olmap = new Map({
target: 'map',
layers: [
baseLayerGroup, rasterLayerGroup, vectorLayer
],
view: new View({
projection: 'EPSG:4326',
center: [ -45.8392, -3.65286 ],
zoom: 8
})
})
...
this.rasterLayerGroup.getLayers().push(
new ImageLayer({
source: new Static({
url: feature.properties.icon,
projection: 'EPSG:4326',
imageExtent: [
feature.properties.bl_longitude, feature.properties.bl_latitude,
feature.properties.tr_longitude, feature.properties.tr_latitude
]
})
})
)
Would someone know how to pass the image bounding box instead of just the image extent?
Thank you in advance.
EDIT 1: Mike's solution
Through Mike's solution I was able to fix a bug that some images have (near to the equator line). For that reason, his answer solved my problem and it inserted the image in a better position that I was expecting in the moment that I created the question.
However, this solution worked to me with images near to the equator line. Images next to the poles stay distorted (Edit 2).
I send below a picture illustrating the final result:
EDIT 2: New problem?
I was testing some images and I have discovered a new bug. Now I have discovered that the image should fit inside the bounding box. If the image does not fit inside the bbox, it stays distorted, such as the print that I send below illustrating.
The image should fit inside the bbox like in the image below [PS 1]:
I believe that it can be a problem of reprojection, but I don't know, because both view projection and image projection is EPSG:4326.
I tried to follow the explanation about Raster Reprojection[1.] on Openlayers site, however I was not able to reproduce it, because, as I said, both projections (view and image) are the same (or they should be).
I send below the GeoJSON that contains the information related to the image above. The image can be found in "properties.icon" (http://www.dpi.inpe.br/newcatalog/tmp/MOD13Q1/2018/MOD13Q1.A2018017.h13v14.jpg). The bbox coordinates can be found in "geometry.coordinates" or in "properties.bl_latitude", "properties.bl_longitude", "properties.br_latitude" and so on.
"bl" means "bottom left", "br" means "bottom right", "tl" means "top left" and "tr" means "top right". These coordinates inside "properties" are the same inside "geometry.coordinates".
{
"geometry": {
"coordinates": [
[
[
-77.7862,
-50
],
[
-100,
-60
],
[
-80,
-60
],
[
-62.229,
-50
],
[
-77.7862,
-50
]
]
],
"type": "Polygon"
},
"properties": {
"alternate": "http://www.dpi.inpe.br/opensearch/v2/granule.json?uid=MOD13Q1.A2018017.h13v14",
"auxpath": null,
"bitslips": null,
"bl_latitude": -60,
"bl_longitude": -100,
"br_latitude": -60,
"br_longitude": -80,
"centerlatitude": -55,
"centerlongitude": -80.0038,
"centertime": null,
"cloud": 0,
"cloudcovermethod": "M",
"dataset": "MOD13Q1",
"date": "2018-01-17T00:00:00",
"enclosure": [
{
"band": "evi",
"radiometric_processing": "SR",
"type": "MOSAIC",
"url": "http://www.dpi.inpe.br/newcatalog/tmp/MOD13Q1/2018/MOD13Q1.A2018017.h13v14.006.2018033223827.hdf"
},
{
"band": "ndvi",
"radiometric_processing": "SR",
"type": "MOSAIC",
"url": "http://www.dpi.inpe.br/newcatalog/tmp/MOD13Q1/2018/MOD13Q1.A2018017.h13v14.006.2018033223827.hdf"
},
...
],
"icon": "http://www.dpi.inpe.br/newcatalog/tmp/MOD13Q1/2018/MOD13Q1.A2018017.h13v14.jpg",
"id": "http://www.dpi.inpe.br/opensearch/v2/granule.json?uid=MOD13Q1.A2018017.h13v14",
"orbit": 0,
"path": 14,
"provider": "OP_CBERS1",
"row": 13,
"satellite": "T1",
"sensor": "MODIS",
"title": "MOD13Q1.A2018017.h13v14",
"tl_latitude": -50,
"tl_longitude": -77.7862,
"tr_latitude": -50,
"tr_longitude": -62.229,
"type": "IMAGES",
"updated": "2018-03-01T18:51:56",
"via": "http://www.dpi.inpe.br/opensearch/v2/metadata/MOD13Q1.A2018017.h13v14"
},
"type": "Feature"
}
Would someone have a new idea?
[PS 1]: The original code that does the image fits inside the bbox is a Leaflet code [2.] and I send it below:
var map = L.map('map').setView([-15.22, -53.23], 5)
...
var anchor = [
[feature.properties.tl_latitude, feature.properties.tl_longitude],
[feature.properties.tr_latitude, feature.properties.tr_longitude],
[feature.properties.br_latitude, feature.properties.br_longitude],
[feature.properties.bl_latitude, feature.properties.bl_longitude]
]
layer._quicklook = L.imageTransform(feature.properties.icon, anchor).addTo(map)
[1.] https://openlayers.org/en/latest/doc/tutorials/raster-reprojection.html
[2.] https://github.com/ScanEx/Leaflet.imageTransform
If the coordinates are those of the photo and the jpg which contains the rotated photo is in EPSG:4326 (i.e. aligned to meridians and parallels) then you need a bounding box containing all of the corners of the photo
import {boundingExtent} from 'ol/extent';
....
this.rasterLayerGroup.getLayers().push(
new ImageLayer({
source: new Static({
url: feature.properties.icon,
projection: 'EPSG:4326',
imageExtent: boundingExtent([
[feature.properties.bl_longitude, feature.properties.bl_latitude],
[feature.properties.br_longitude, feature.properties.br_latitude],
[feature.properties.tl_longitude, feature.properties.tl_latitude],
[feature.properties.tr_longitude, feature.properties.tr_latitude]
])
})
})
)
However your top screenshot has the jpg itself rotated. If that is desired the projection isn't EPSG:4326 and you would need to define a custom projection to handle the rotation.
I've managed to get something close, but simply stretching the image to fit the polygon doesn't give the exact alignment at the side that the leaflet method does
var properties = {
"bl_latitude": -60,
"bl_longitude": -100,
"br_latitude": -60,
"br_longitude": -80,
"centerlatitude": -55,
"centerlongitude": -80.0038,
"icon": "https://www.mikenunn.net/demo/MOD13Q1.A2018017.h13v14.jpg",
"tl_latitude": -50,
"tl_longitude": -77.7862,
"tr_latitude": -50,
"tr_longitude": -62.229,
};
function overlaySource ( properties ) {
var projection = ol.proj.get('EPSG:3857'); // leaflet projection
var extentSize = [0, 0, 4096, 4096]; // arbitary extent for the projection transforms
var size0 = extentSize[2];
var size1 = extentSize[3];
var url = properties.icon;
var bl = ol.proj.transform([properties.bl_longitude, properties.bl_latitude], 'EPSG:4326', projection);
var tl = ol.proj.transform([properties.tl_longitude, properties.tl_latitude], 'EPSG:4326', projection);
var br = ol.proj.transform([properties.br_longitude, properties.br_latitude], 'EPSG:4326', projection);
var tr = ol.proj.transform([properties.tr_longitude, properties.tr_latitude], 'EPSG:4326', projection);
function normalTransform(coordinates, output, dimensions) {
var dims = dimensions || 2;
for (var i=0; i<coordinates.length; i+=dims) {
var left = bl[0] + (tl[0]-bl[0]) * coordinates[i+1]/size1;
var right = br[0] + (tr[0]-br[0]) * coordinates[i+1]/size1;
var top = tl[1] + (tr[1]-tl[1]) * coordinates[i]/size0;
var bottom = bl[1] + (br[1]-bl[1]) * coordinates[i]/size0;
var newCoordinates0 = left + (right-left) * coordinates[i]/size0;
var newCoordinates1 = bottom + (top-bottom) * coordinates[i+1]/size1;
c = ol.proj.transform([newCoordinates0, newCoordinates1], projection, 'EPSG:3857');
//console.log(coordinates[i] + ' ' + coordinates[i+1] + ' ' + c[0] + ' ' + c[1]);
coordinates[i] = c[0];
coordinates[i+1] = c[1];
}
return coordinates;
}
function rotateTransform(coordinates, output, dimensions) {
var dims = dimensions || 2;
for (var i=0; i<coordinates.length; i+=dims) {
c = ol.proj.transform([coordinates[i], coordinates[i+1]], 'EPSG:3857', projection);
var left = bl[0] + (tl[0]-bl[0]) * (c[1]-bl[1]) /(tl[1]-bl[1]);
var right = br[0] + (tr[0]-br[0]) * (c[1]-br[1]) /(tr[1]-br[1]);
var top = tl[1] + (tr[1]-tl[1]) * (c[0]-tl[0])/(tr[0]-tl[0]);
var bottom = bl[1] + (br[1]-bl[1]) * (c[0]-bl[0])/(br[0]-bl[0]);
var newCoordinates0 = (c[0]-left)*size0/(right-left);
var newCoordinates1 = (c[1]-bottom)*size1/(top-bottom);
//console.log(coordinates[i] + ' ' + coordinates[i+1] + ' ' + newCoordinates0 + ' ' + newCoordinates1);
coordinates[i] = newCoordinates0;
coordinates[i+1] = newCoordinates1;
}
return coordinates;
}
var rotatedProjection = new ol.proj.Projection({
code: 'EPSG:' + url + 'rotated',
units: 'm',
extent: extentSize
});
ol.proj.addProjection(rotatedProjection);
ol.proj.addCoordinateTransforms('EPSG:3857', rotatedProjection,
function(coordinate) {
return rotateTransform(coordinate);
},
function(coordinate) {
return normalTransform(coordinate);
}
);
ol.proj.addCoordinateTransforms('EPSG:4326', rotatedProjection,
function(coordinate) {
return rotateTransform(ol.proj.transform(coordinate, "EPSG:4326", "EPSG:3857"));
},
function(coordinate) {
return ol.proj.transform(normalTransform(coordinate), "EPSG:3857", "EPSG:4326");
}
);
return new ol.source.ImageStatic({
projection: rotatedProjection,
imageExtent: extentSize,
url: url
});
}
var tileLayer = new ol.layer.Tile({
source: new ol.source.XYZ({
attributions: [
'Powered by Esri',
'Source: Esri, DigitalGlobe, GeoEye, Earthstar Geographics, CNES/Airbus DS, USDA, USGS, AeroGRID, IGN, and the GIS User Community'
],
//attributionsCollapsible: false,
url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
maxZoom: 23
})
});
var imageLayer = new ol.layer.Image({
source: overlaySource( properties ),
opacity: 0.7
})
var map = new ol.Map({
layers: [tileLayer, imageLayer],
target: 'map',
logo: false,
view: new ol.View()
});
var imageProj = imageLayer.getSource().getProjection();
map.getView().fit(ol.proj.transformExtent(imageProj.getExtent(), imageProj, map.getView().getProjection()), {constrainResolution: false});
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.5.0/proj4.js"></script>
<div id="map" class="map"></div>
This is my KML method, but that is a simple rotation of a rectangle by a specified angle, not warping it into a quadrilateral where only two of the sides are parallel.
function kmlOverlaySource ( kmlExtent, // KMLs specify the extent the unrotated image would occupy
url,
rotation,
imageSize,
) {
// calculate latitude of true scale of equidistant cylindrical projection based on pixels per degree on each axis
proj4.defs('EPSG:' + url, '+proj=eqc +lat_ts=' +
(Math.acos((ol.extent.getHeight(kmlExtent)/imageSize[1])
/(ol.extent.getWidth(kmlExtent)/imageSize[0]))*180/Math.PI) +
' +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs');
if (ol.proj.proj4 && ol.proj.proj4.register) { ol.proj.proj4.register(proj4); } // if OL5 register proj4
// convert the extents to source projection coordinates
var projection = ol.proj.get('EPSG:' + url);
var projExtent = ol.proj.transformExtent(kmlExtent, 'EPSG:4326', projection);
var angle = -rotation * Math.PI/180;
function rotateTransform(coordinates, output, dimensions) {
var dims = dimensions || 2;
for (var i=0; i<coordinates.length; i+=dims) {
var point = new ol.geom.Point([coordinates[i],coordinates[i+1]]);
point.rotate(angle, ol.extent.getCenter(projExtent));
var newCoordinates = point.getCoordinates();
coordinates[i] = newCoordinates[0];
coordinates[i+1] = newCoordinates[1];
}
return coordinates;
}
function normalTransform(coordinates, output, dimensions) {
var dims = dimensions || 2;
for (var i=0; i<coordinates.length; i+=dims) {
var point = new ol.geom.Point([coordinates[i],coordinates[i+1]]);
point.rotate(-angle, ol.extent.getCenter(projExtent));
var newCoordinates = point.getCoordinates();
coordinates[i] = newCoordinates[0];
coordinates[i+1] = newCoordinates[1];
}
return coordinates;
}
var rotatedProjection = new ol.proj.Projection({
code: 'EPSG:' + url + 'rotated',
units: 'm',
extent: projExtent
});
ol.proj.addProjection(rotatedProjection);
ol.proj.addCoordinateTransforms('EPSG:4326', rotatedProjection,
function(coordinate) {
return rotateTransform(ol.proj.transform(coordinate, 'EPSG:4326', projection));
},
function(coordinate) {
return ol.proj.transform(normalTransform(coordinate), projection, 'EPSG:4326');
}
);
ol.proj.addCoordinateTransforms('EPSG:3857', rotatedProjection,
function(coordinate) {
return rotateTransform(ol.proj.transform(coordinate, 'EPSG:3857', projection));
},
function(coordinate) {
return ol.proj.transform(normalTransform(coordinate), projection, 'EPSG:3857');
}
);
return new ol.source.ImageStatic({
projection: rotatedProjection,
url: url,
imageExtent: projExtent
});
}
var tileLayer = new ol.layer.Tile({
source: new ol.source.XYZ({
attributions: [
'Powered by Esri',
'Source: Esri, DigitalGlobe, GeoEye, Earthstar Geographics, CNES/Airbus DS, USDA, USGS, AeroGRID, IGN, and the GIS User Community'
],
//attributionsCollapsible: false,
url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
maxZoom: 23
})
});
// these would normally be parsed from a KML file
var kmlExtent = [8.433995415151397, 46.65804355828784, 9.144871415151389, 46.77980155828784];
var url = 'https://raw.githubusercontent.com/ReneNyffenegger/about-GoogleEarth/master/kml/the_png_says.png'
var rotation = 30;
var imageSize = [];
var img = document.createElement('img');
img.onload = imageLoaded;
img.src = url;
function imageLoaded() {
imageSize[0] = img.width;
imageSize[1] = img.height;
var imageLayer = new ol.layer.Image({
source: kmlOverlaySource(kmlExtent, url, rotation, imageSize),
});
var map = new ol.Map({
layers: [tileLayer, imageLayer],
target: 'map',
logo: false,
view: new ol.View()
});
var imageProj = imageLayer.getSource().getProjection();
map.getView().fit(ol.proj.transformExtent(imageProj.getExtent(), imageProj, map.getView().getProjection()), {constrainResolution: false});
}
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.5.0/proj4.js"></script>
<div id="map" class="map"></div>
I have been using the google maps api and i need to display from what i understand is a standard overlay that displays the Address and links of "how to arrive from here here", "to here", "apply zoom here" etc.
I got the overlay working like this but its not standard, i can customize it... is there a way to insert the standard overlay as explained above?
Here is my code for inserting a custom overlay
var marker = new GMarker(point); // Create the marker
map.addOverlay(marker); // And add it to the map
// And open some infowindow, with some HTML text in it
marker.openInfoWindowHtml(
'This is my test!!!, <strong>test </strong>'
);
Any help really appeciated
Thanks
As far as I know there is no standard overlay. Here's the code that'll help. You may add functionality by changing info parameter as desired.
if( GBrowserIsCompatible() ) {
walkmap = new GMap2( document.getElementById( "walkmap" ) ) ;
walkmap.setCenter( new GLatLng( 11.22,-33.44 ), 16 ) ;
walkmap.setMapType( G_HYBRID_MAP ) ;
var baseIcon = new GIcon();
baseIcon.shadow = "http://www.google.com/mapfiles/shadow50.png";
baseIcon.iconSize = new GSize(20, 34);
baseIcon.shadowSize = new GSize(37, 34);
baseIcon.iconAnchor = new GPoint(9, 34);
baseIcon.infoWindowAnchor = new GPoint(9, 34);
baseIcon.infoShadowAnchor = new GPoint(18, 25);
var Marker = function( point, info, image ) {
var point = point ;
var icon = new GIcon( baseIcon ) ;
icon.image = image ;
var marker = new GMarker( point, icon ) ;
marker.info = info ;
marker.showInfo = function() {
this.openInfoWindowHtml( this.info ) ;
}
GEvent.addListener( marker, "click", function() {
marker.showInfo() ;
});
walkmap.addOverlay( marker ) ;
return marker ;
}
new Marker( new GLatLng( 11.22,-33.44 ), "My marker", "http://www.google.com/intl/en_us/mapfiles/dd-start.png" ) ;
}
I am displaying the different marker on the map , the problem is that sometimes (specially when I reset the web server) the map is loaded properly and it even shows the shadow of the points but the markers are not shown/visible on the map.However on the subsequent calls the markers are shown properly (perhaps cached , but not sure). This behavior is consistent in all browsers namely IE 6/7/8 , Chrome , Firfox 3.5.6.
The javascript shown below creates the marker. On the sideline, as markers can be of different sizes, I need to first determine there width and size (other wise they look deformed).
var imgTemp = new Image();
imgTemp.name = "img_" + i.toString();
imgTemp.src = groupMarkerUrl; //url to the actual image
point = new GLatLng(parseFloat(latitude), parseFloat(longitude));
var icon = new GIcon(G_DEFAULT_ICON);
icon.image = groupMarkerUrl;
icon.iconSize = new GSize(imgTemp.width, imgTemp.height); //Width x Height
icon.iconAnchor = new GPoint(14, 15);
icon.infoWindowAnchor = new GPoint(5, 1);
marker = new GMarker(point, icon);
map.setCenter(point, 13);
//build the information box
var htmlContent = "<div style=\"color:#000000\"><span style=\"font-weight:bold;\">" + title + "</span><br/>";
if (address != "") {
htmlContent += address + " ";
}
if (zipcode != "") {
htmlContent += "<br/>" + zipcode + ", ";
}
if (city != "") {
htmlContent += city;
}
if (telephone != "") {
htmlContent += "<br/>Tel : " + telephone;
}
if (fax != "") {
htmlContent += "<br/>Fax : " + fax;
}
htmlContent += "</div>";
map.addOverlay(marker);
markerKeys.push(stamp);
markers[stamp] = marker;
//Add legends with group markers one for each group
if (null == legends[groupId]) {
legends[groupId] = groupMarkerUrl;
var nbsp = document.createTextNode(" ");
var image = document.createElement("img");
image.setAttribute("src", groupMarkerUrl);
image.setAttribute("style", "margin-left:10px !important; border:\"0\";");
pushpinPnlConsole.appendChild(nbsp);
pushpinPnlConsole.appendChild(image);
pushpinPnlConsole.setAttribute("style", "display:block");
}
eval("GEvent.addListener(markers[stamp] , \"click\", function(){markers['" + stamp + "'].openInfoWindowHtml(windowHtmls['" + stamp + "']);});");
windowHtmls[stamp] = htmlContent;
opticianTBody.appendChild(row);
Thanks.
Your problem is that
imgTemp.src = groupMarkerUrl; //url to the actual image
takes some time to complete. Since you use imgTemp.width and imgTemp.height without waiting for the image to load, those values are likely to be zero. The API will plot your icons at zero size.
In browsers other than MSIE, you can omit the icon.iconSize (and not copy the details from G_DEFAULT_ICON as mentioned by mopoke) and the marker will default to the image size if the image has arrived by the time the marker gets displayed. In MSIE, for PNG images, the API uses the AplphaImageLoader which defaults to size zero if a size is not specified.
The workround is to preload your images properly, by placing this code inline, so that it gets executed before the onload event
var imgTemp = new Image();
imgTemp.name = "img_" + i.toString();
imgTemp.src = groupMarkerUrl; //url to the actual image
And placing your icon creation code in an onload function, so that it doesn't get executed until after all the images loaded by the inline code have been completely fetched.
Not sure why you're using G_DEFAULT_ICON in your constructor.
To do a custom icon, use something like:
var icon = new GIcon();
icon.image = groupMarkerUrl;
//...
After you reset the server and you try and load the image referenced in groupMarkerUrl, do you see it correctly?