Antony
Antony

Reputation: 1318

Openlayers 3: Drawing grid lines (graticule) with predefined units on the custom static image

I am trying to draw custom x-y axes grid lines on top of a static image, i.e. image pixels rather than lattitude and longitudes. Ideally, the grid lines should be redrawn dynamically when I drag/zoom/scroll the image, just like the x-y ruler bars in Photoshop.

I came across the following code example, which provides a custom projection function to directly map image pixel coordinates to map coordinates.

http://openlayers.org/en/latest/examples/static-image.html

// Map views always need a projection.  Here we just want to map image
// coordinates directly to map coordinates, so we create a projection that uses
// the image extent in pixels.
var extent = [0, 0, 1024, 968];
var projection = new ol.proj.Projection({
        code: 'xkcd-image',
        units: 'pixels',
        extent: extent
      });

I tried to append the following code to the script. However, the ol.Graticule class seems to be incompatible with the custom ol.proj.Projection definition.

http://openlayers.org/en/latest/examples/graticule.html

// Create the graticule component
var graticule = new ol.Graticule({
// the style to use for the lines, optional.
strokeStyle: new ol.style.Stroke({
  color: 'rgba(255,120,0,0.9)',
  width: 2,
  lineDash: [0.5, 4]
  })
});
graticule.setMap(map);

What's wrong with the above code?

P.S. I am aware of the Openseadragon API which provides a dynamic scalebar. However, I wish to stick to Openlayers API because I also have an extra map layer of anchor points at predefined locations on the static image.

Upvotes: 3

Views: 3309

Answers (1)

Jose Hermosilla Rodrigo
Jose Hermosilla Rodrigo

Reputation: 3683

I had the same problem. For this to work I created a Vector Layer, (where axis are drawn). To draw the axis, I need to listen to View changes.

Whenever the view changes, calculate the actual extent for the view.

With extent information and ([width, height] of the image, you can then draw axis)

let listenerAxis = null,
    w = 0,
    h = 0

const xAxisStyle = new ol.style.Style({
  stroke: new ol.style.Stroke({
    color: 'red',
    width: 2
  })
})

const yAxisStyle = new ol.style.Style({
  stroke: new ol.style.Stroke({
    color: 'green',
    width: 2
  })
})


const ImageLayer = new ol.layer.Image()

const AxisLayer = new ol.layer.Vector({ source: new ol.source.Vector() })

AxisLayer.setStyle((feature, resolution) => {
  if(feature.getProperties().axis == 'x') {
    return xAxisStyle
  }
  return yAxisStyle
})

const renderer = new ol.Map({
  target: 'map',
  layers: [ImageLayer]
})

AxisLayer.setMap(renderer)

processFile('https://i2.wp.com/beebom.com/wp-content/uploads/2016/01/Reverse-Image-Search-Engines-Apps-And-Its-Uses-2016.jpg?resize=640%2C426')



function removeAxis() {
  AxisLayer.getSource().clear()
  ol.Observable.unByKey(listenerAxis)
  listenerAxis = null
}

function drawAxis() {
  function draw(){
    AxisLayer.getSource().clear()
    const extent = renderer.getView().calculateExtent()
    const [xmin, ymin, xmax, ymax] = extent
    // Eje X
    const axisX = new ol.geom.LineString([ [xmin, h / 2], [xmax, h / 2] ])
    const axisY = new ol.geom.LineString([ [w / 2, ymin], [w / 2, ymax] ])

    const featureX = new ol.Feature({ geometry: axisX, axis: 'x' })
    const featureY = new ol.Feature({ geometry: axisY, axis: 'y' })

    AxisLayer.getSource().addFeatures([featureX, featureY])
  }

  listenerAxis = renderer.getView().on('change', draw)
  draw()

}


async function processFile(path) {

  ImageLayer.setSource()
  removeAxis()

  if(!path) {
    return 
  }


  const [wi, hi] = await readImage(path)
  w = wi
  h = hi

  const source = getImageStatic(path, w, h)
  const view = getViewForImage(w, h)


  ImageLayer.setSource(source)
  renderer.setView(view)
  drawAxis()
}


// Some helpers
function readImage(localPath) {
  const img = document.createElement('img')
  return new Promise((res, rej) => {
    img.src = localPath
    img.addEventListener('load', (event) => {
      const { naturalWidth, naturalHeight } = img
      console.log('img', naturalWidth, naturalHeight)
      res([naturalWidth, naturalHeight])
    })
  })
}

function getViewForImage(w, h) {
  return new ol.View({
    center: [w / 2, h / 2],
    zoom: 2,
    projection: new ol.proj.Projection({
      extent: [0, 0, w, h],
      units: 'pixels'
    }),
    extent: [0, 0, w, h]
  })
}

function getImageStatic(path, w, h) {
  return new ol.source.ImageStatic({
    url: path,
    imageExtent: [0, 0, w, h]
  })
}
#map {
  width: 100%;
  height: 100%;
  background: grey;
}
<link href="https://openlayers.org/en/v4.6.5/css/ol.css" rel="stylesheet"/>
<script src="https://openlayers.org/en/v4.6.5/build/ol.js"></script>

<div id="map"></div>

Upvotes: 3

Related Questions