Add a rotated satellite image on the map using OpenLayers 5Displaying georeferenced images using OpenLayers 5How to reproject a vector layer when you switch between base maps of different projectionsBackground Layer in EPSG:900913 causes WMS overlays to failUsing OSM and EPSG:4326 with openlayers increase latitude in near 90 degrees. Why?how to project a flat image to spherical mercator EPGS:900013 to use in a Image Layer in openlayersHere Map Tile API: Retrieve Map tile address from EPSG:3857 coordinateSimple Openlayers + AngularChanging projection in an OpenLayers 2.13 map objectWMS Layer provide by Map Sever rendering incorrectly in ol3Reproject EPSG:28992 to EPSG:3857 (defaults to EPSG:4326)OpenLayers 4 cant´t reproject json file

Function over a list that depends on the index

Found old paper shares of Motorola Inc that has since been broken up

How did Jayne know when to shoot?

What are my hardware upgrade optoins for a late 2009 iMac?

What makes MOVEQ quicker than a normal MOVE in 68000 assembly?

What is the function of "mal" in saying "Das nenn ich mal ein X"?

What's the physical meaning of the statement that "photons don't have positions"?

Are there any satellites in geosynchronous but not geostationary orbits?

Why is this guy handcuffed censored?

Is encryption still applied if you ignore the SSL certificate warning for self-signed certs?

Why teach C using scanf without talking about command line arguments?

Should I have one hand on the throttle during engine ignition?

I want light controlled by one switch, not two

The most secure way to handle someone forgetting to verify their account?

Doesn't the Schrödinger's cat inside the box cause the probability wave function to collapse long before a human opens the box?

Applying for jobs with an obvious scar

What is a Romeo Word™?

Formating slide

How was Luke's prosthetic hand in Episode V filmed?

Does the Bracer of Flying Daggers really let a thief make 4 attacks per round?

What is a Kravchuk transform and how is it related to Fourier transforms?

Do pedestrians imitate automotive traffic?

Last-minute canceled work-trip means I'll lose thousands of dollars on planned vacation

"This used to be my phone number"



Add a rotated satellite image on the map using OpenLayers 5


Displaying georeferenced images using OpenLayers 5How to reproject a vector layer when you switch between base maps of different projectionsBackground Layer in EPSG:900913 causes WMS overlays to failUsing OSM and EPSG:4326 with openlayers increase latitude in near 90 degrees. Why?how to project a flat image to spherical mercator EPGS:900013 to use in a Image Layer in openlayersHere Map Tile API: Retrieve Map tile address from EPSG:3857 coordinateSimple Openlayers + AngularChanging projection in an OpenLayers 2.13 map objectWMS Layer provide by Map Sever rendering incorrectly in ol3Reproject EPSG:28992 to EPSG:3857 (defaults to EPSG:4326)OpenLayers 4 cant´t reproject json file






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;








0















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:



enter image description here



The result that I've got was that:



enter image description here



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:



enter image description here



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.



enter image description here



The image should fit inside the bbox like in the image below [PS 1]:



enter image description here



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










share|improve this question
























  • Do you have lon/lat values for tl and br or a rotation value?

    – Mike
    Mar 26 at 20:35











  • Yes, I do lon/lat values for the four points of the image bounding box. They are "bl_latitude, bl_longitude, br_latitude, br_longitude, tl_latitude, tl_longitude, tr_latitude, tr_longitude". A rotation value I do not have. Should I calculate it by the bbox values?

    – rmmariano
    Mar 27 at 11:38











  • Does it not belong to GIS SE site?

    – Gilles-Antoine Nys
    Mar 29 at 16:35











  • "Ask a question", on OpenLayers site, redirects to StackOverflow instead of GIS SE.

    – rmmariano
    Mar 29 at 17:25

















0















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:



enter image description here



The result that I've got was that:



enter image description here



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:



enter image description here



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.



enter image description here



The image should fit inside the bbox like in the image below [PS 1]:



enter image description here



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










share|improve this question
























  • Do you have lon/lat values for tl and br or a rotation value?

    – Mike
    Mar 26 at 20:35











  • Yes, I do lon/lat values for the four points of the image bounding box. They are "bl_latitude, bl_longitude, br_latitude, br_longitude, tl_latitude, tl_longitude, tr_latitude, tr_longitude". A rotation value I do not have. Should I calculate it by the bbox values?

    – rmmariano
    Mar 27 at 11:38











  • Does it not belong to GIS SE site?

    – Gilles-Antoine Nys
    Mar 29 at 16:35











  • "Ask a question", on OpenLayers site, redirects to StackOverflow instead of GIS SE.

    – rmmariano
    Mar 29 at 17:25













0












0








0








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:



enter image description here



The result that I've got was that:



enter image description here



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:



enter image description here



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.



enter image description here



The image should fit inside the bbox like in the image below [PS 1]:



enter image description here



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










share|improve this question
















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:



enter image description here



The result that I've got was that:



enter image description here



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:



enter image description here



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.



enter image description here



The image should fit inside the bbox like in the image below [PS 1]:



enter image description here



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







gis openlayers openlayers-5 satellite-image






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Mar 28 at 12:22







rmmariano

















asked Mar 26 at 11:43









rmmarianormmariano

12510 bronze badges




12510 bronze badges












  • Do you have lon/lat values for tl and br or a rotation value?

    – Mike
    Mar 26 at 20:35











  • Yes, I do lon/lat values for the four points of the image bounding box. They are "bl_latitude, bl_longitude, br_latitude, br_longitude, tl_latitude, tl_longitude, tr_latitude, tr_longitude". A rotation value I do not have. Should I calculate it by the bbox values?

    – rmmariano
    Mar 27 at 11:38











  • Does it not belong to GIS SE site?

    – Gilles-Antoine Nys
    Mar 29 at 16:35











  • "Ask a question", on OpenLayers site, redirects to StackOverflow instead of GIS SE.

    – rmmariano
    Mar 29 at 17:25

















  • Do you have lon/lat values for tl and br or a rotation value?

    – Mike
    Mar 26 at 20:35











  • Yes, I do lon/lat values for the four points of the image bounding box. They are "bl_latitude, bl_longitude, br_latitude, br_longitude, tl_latitude, tl_longitude, tr_latitude, tr_longitude". A rotation value I do not have. Should I calculate it by the bbox values?

    – rmmariano
    Mar 27 at 11:38











  • Does it not belong to GIS SE site?

    – Gilles-Antoine Nys
    Mar 29 at 16:35











  • "Ask a question", on OpenLayers site, redirects to StackOverflow instead of GIS SE.

    – rmmariano
    Mar 29 at 17:25
















Do you have lon/lat values for tl and br or a rotation value?

– Mike
Mar 26 at 20:35





Do you have lon/lat values for tl and br or a rotation value?

– Mike
Mar 26 at 20:35













Yes, I do lon/lat values for the four points of the image bounding box. They are "bl_latitude, bl_longitude, br_latitude, br_longitude, tl_latitude, tl_longitude, tr_latitude, tr_longitude". A rotation value I do not have. Should I calculate it by the bbox values?

– rmmariano
Mar 27 at 11:38





Yes, I do lon/lat values for the four points of the image bounding box. They are "bl_latitude, bl_longitude, br_latitude, br_longitude, tl_latitude, tl_longitude, tr_latitude, tr_longitude". A rotation value I do not have. Should I calculate it by the bbox values?

– rmmariano
Mar 27 at 11:38













Does it not belong to GIS SE site?

– Gilles-Antoine Nys
Mar 29 at 16:35





Does it not belong to GIS SE site?

– Gilles-Antoine Nys
Mar 29 at 16:35













"Ask a question", on OpenLayers site, redirects to StackOverflow instead of GIS SE.

– rmmariano
Mar 29 at 17:25





"Ask a question", on OpenLayers site, redirects to StackOverflow instead of GIS SE.

– rmmariano
Mar 29 at 17:25












1 Answer
1






active

oldest

votes


















1














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) 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) 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) 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 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>








share|improve this answer

























  • Your solution worked to my images that are next to equator lines, but the ones that are next to the poles stay distorted, unfortunately. Now I have discovered that the images should fit inside the bounding box passed (bl_longitude, bl_latitude, ...). Would you have a new tip that you would like to share? Thank you so much for your reply.

    – rmmariano
    Mar 27 at 18:30












  • Can you post the original image for that part of South America and all the coordinates that go with it. Clearly it's going to need a rotated projection of some kind. I've managed those for KML ground overlays designed for Google Earth renenyffenegger.ch/notes/tools/Google-Earth/kml/index to work with OpenLayers i.stack.imgur.com/68bYZ.jpg so something similar may be possible with yours.

    – Mike
    Mar 27 at 20:45











  • I updated the question including what you would like. Really, both cases seem similar. How did you do to rotate the PNG image? In the question I included a Leaflet code that plots that South America image on the map. Thank you so much.

    – rmmariano
    Mar 28 at 12:32






  • 1





    I've adding working snippets for an attempt at reproducing the leaflet result and for the KML overlay..

    – Mike
    Mar 29 at 16:34











  • Thank you Mike so much for your help.

    – rmmariano
    Mar 29 at 17:27










Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55356335%2fadd-a-rotated-satellite-image-on-the-map-using-openlayers-5%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









1














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) 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) 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) 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 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>








share|improve this answer

























  • Your solution worked to my images that are next to equator lines, but the ones that are next to the poles stay distorted, unfortunately. Now I have discovered that the images should fit inside the bounding box passed (bl_longitude, bl_latitude, ...). Would you have a new tip that you would like to share? Thank you so much for your reply.

    – rmmariano
    Mar 27 at 18:30












  • Can you post the original image for that part of South America and all the coordinates that go with it. Clearly it's going to need a rotated projection of some kind. I've managed those for KML ground overlays designed for Google Earth renenyffenegger.ch/notes/tools/Google-Earth/kml/index to work with OpenLayers i.stack.imgur.com/68bYZ.jpg so something similar may be possible with yours.

    – Mike
    Mar 27 at 20:45











  • I updated the question including what you would like. Really, both cases seem similar. How did you do to rotate the PNG image? In the question I included a Leaflet code that plots that South America image on the map. Thank you so much.

    – rmmariano
    Mar 28 at 12:32






  • 1





    I've adding working snippets for an attempt at reproducing the leaflet result and for the KML overlay..

    – Mike
    Mar 29 at 16:34











  • Thank you Mike so much for your help.

    – rmmariano
    Mar 29 at 17:27















1














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) 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) 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) 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 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>








share|improve this answer

























  • Your solution worked to my images that are next to equator lines, but the ones that are next to the poles stay distorted, unfortunately. Now I have discovered that the images should fit inside the bounding box passed (bl_longitude, bl_latitude, ...). Would you have a new tip that you would like to share? Thank you so much for your reply.

    – rmmariano
    Mar 27 at 18:30












  • Can you post the original image for that part of South America and all the coordinates that go with it. Clearly it's going to need a rotated projection of some kind. I've managed those for KML ground overlays designed for Google Earth renenyffenegger.ch/notes/tools/Google-Earth/kml/index to work with OpenLayers i.stack.imgur.com/68bYZ.jpg so something similar may be possible with yours.

    – Mike
    Mar 27 at 20:45











  • I updated the question including what you would like. Really, both cases seem similar. How did you do to rotate the PNG image? In the question I included a Leaflet code that plots that South America image on the map. Thank you so much.

    – rmmariano
    Mar 28 at 12:32






  • 1





    I've adding working snippets for an attempt at reproducing the leaflet result and for the KML overlay..

    – Mike
    Mar 29 at 16:34











  • Thank you Mike so much for your help.

    – rmmariano
    Mar 29 at 17:27













1












1








1







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) 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) 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) 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 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>








share|improve this answer















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) 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) 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) 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 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>








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) 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) 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>





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) 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) 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>





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) 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 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>





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) 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 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>






share|improve this answer














share|improve this answer



share|improve this answer








edited Mar 29 at 16:31

























answered Mar 27 at 12:34









MikeMike

4,4042 gold badges2 silver badges14 bronze badges




4,4042 gold badges2 silver badges14 bronze badges












  • Your solution worked to my images that are next to equator lines, but the ones that are next to the poles stay distorted, unfortunately. Now I have discovered that the images should fit inside the bounding box passed (bl_longitude, bl_latitude, ...). Would you have a new tip that you would like to share? Thank you so much for your reply.

    – rmmariano
    Mar 27 at 18:30












  • Can you post the original image for that part of South America and all the coordinates that go with it. Clearly it's going to need a rotated projection of some kind. I've managed those for KML ground overlays designed for Google Earth renenyffenegger.ch/notes/tools/Google-Earth/kml/index to work with OpenLayers i.stack.imgur.com/68bYZ.jpg so something similar may be possible with yours.

    – Mike
    Mar 27 at 20:45











  • I updated the question including what you would like. Really, both cases seem similar. How did you do to rotate the PNG image? In the question I included a Leaflet code that plots that South America image on the map. Thank you so much.

    – rmmariano
    Mar 28 at 12:32






  • 1





    I've adding working snippets for an attempt at reproducing the leaflet result and for the KML overlay..

    – Mike
    Mar 29 at 16:34











  • Thank you Mike so much for your help.

    – rmmariano
    Mar 29 at 17:27

















  • Your solution worked to my images that are next to equator lines, but the ones that are next to the poles stay distorted, unfortunately. Now I have discovered that the images should fit inside the bounding box passed (bl_longitude, bl_latitude, ...). Would you have a new tip that you would like to share? Thank you so much for your reply.

    – rmmariano
    Mar 27 at 18:30












  • Can you post the original image for that part of South America and all the coordinates that go with it. Clearly it's going to need a rotated projection of some kind. I've managed those for KML ground overlays designed for Google Earth renenyffenegger.ch/notes/tools/Google-Earth/kml/index to work with OpenLayers i.stack.imgur.com/68bYZ.jpg so something similar may be possible with yours.

    – Mike
    Mar 27 at 20:45











  • I updated the question including what you would like. Really, both cases seem similar. How did you do to rotate the PNG image? In the question I included a Leaflet code that plots that South America image on the map. Thank you so much.

    – rmmariano
    Mar 28 at 12:32






  • 1





    I've adding working snippets for an attempt at reproducing the leaflet result and for the KML overlay..

    – Mike
    Mar 29 at 16:34











  • Thank you Mike so much for your help.

    – rmmariano
    Mar 29 at 17:27
















Your solution worked to my images that are next to equator lines, but the ones that are next to the poles stay distorted, unfortunately. Now I have discovered that the images should fit inside the bounding box passed (bl_longitude, bl_latitude, ...). Would you have a new tip that you would like to share? Thank you so much for your reply.

– rmmariano
Mar 27 at 18:30






Your solution worked to my images that are next to equator lines, but the ones that are next to the poles stay distorted, unfortunately. Now I have discovered that the images should fit inside the bounding box passed (bl_longitude, bl_latitude, ...). Would you have a new tip that you would like to share? Thank you so much for your reply.

– rmmariano
Mar 27 at 18:30














Can you post the original image for that part of South America and all the coordinates that go with it. Clearly it's going to need a rotated projection of some kind. I've managed those for KML ground overlays designed for Google Earth renenyffenegger.ch/notes/tools/Google-Earth/kml/index to work with OpenLayers i.stack.imgur.com/68bYZ.jpg so something similar may be possible with yours.

– Mike
Mar 27 at 20:45





Can you post the original image for that part of South America and all the coordinates that go with it. Clearly it's going to need a rotated projection of some kind. I've managed those for KML ground overlays designed for Google Earth renenyffenegger.ch/notes/tools/Google-Earth/kml/index to work with OpenLayers i.stack.imgur.com/68bYZ.jpg so something similar may be possible with yours.

– Mike
Mar 27 at 20:45













I updated the question including what you would like. Really, both cases seem similar. How did you do to rotate the PNG image? In the question I included a Leaflet code that plots that South America image on the map. Thank you so much.

– rmmariano
Mar 28 at 12:32





I updated the question including what you would like. Really, both cases seem similar. How did you do to rotate the PNG image? In the question I included a Leaflet code that plots that South America image on the map. Thank you so much.

– rmmariano
Mar 28 at 12:32




1




1





I've adding working snippets for an attempt at reproducing the leaflet result and for the KML overlay..

– Mike
Mar 29 at 16:34





I've adding working snippets for an attempt at reproducing the leaflet result and for the KML overlay..

– Mike
Mar 29 at 16:34













Thank you Mike so much for your help.

– rmmariano
Mar 29 at 17:27





Thank you Mike so much for your help.

– rmmariano
Mar 29 at 17:27








Got a question that you can’t ask on public Stack Overflow? Learn more about sharing private information with Stack Overflow for Teams.







Got a question that you can’t ask on public Stack Overflow? Learn more about sharing private information with Stack Overflow for Teams.



















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55356335%2fadd-a-rotated-satellite-image-on-the-map-using-openlayers-5%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Kamusi Yaliyomo Aina za kamusi | Muundo wa kamusi | Faida za kamusi | Dhima ya picha katika kamusi | Marejeo | Tazama pia | Viungo vya nje | UrambazajiKuhusu kamusiGo-SwahiliWiki-KamusiKamusi ya Kiswahili na Kiingerezakuihariri na kuongeza habari

Swift 4 - func physicsWorld not invoked on collision? The Next CEO of Stack OverflowHow to call Objective-C code from Swift#ifdef replacement in the Swift language@selector() in Swift?#pragma mark in Swift?Swift for loop: for index, element in array?dispatch_after - GCD in Swift?Swift Beta performance: sorting arraysSplit a String into an array in Swift?The use of Swift 3 @objc inference in Swift 4 mode is deprecated?How to optimize UITableViewCell, because my UITableView lags

Access current req object everywhere in Node.js ExpressWhy are global variables considered bad practice? (node.js)Using req & res across functionsHow do I get the path to the current script with Node.js?What is Node.js' Connect, Express and “middleware”?Node.js w/ express error handling in callbackHow to access the GET parameters after “?” in Express?Modify Node.js req object parametersAccess “app” variable inside of ExpressJS/ConnectJS middleware?Node.js Express app - request objectAngular Http Module considered middleware?Session variables in ExpressJSAdd properties to the req object in expressjs with Typescript