OpenLayers JavaScript – Combining Static Image Layer with Vector Layer in OpenLayers 6

imagejavascriptopenlayersvector

I'm trying to have an image layer combined with a vector layer but when I add projection to View the vectors disappear. Am I doing something wrong?

Here is an example of what I'm trying to achieve: https://stackblitz.com/edit/react-ol6

I want these two layers below to be together in View.

Vector Layer

Image Layer

import React, { Component } from "react";
import ReactDOM from "react-dom"

import "./style.css";
import "ol/ol.css";
import { Map, View } from "ol";
import { getCenter } from "ol/extent";
import ImageLayer from "ol/layer/Image";

import Feature from "ol/Feature";
import Point from "ol/geom/Point";
import VectorLayer from "ol/layer/Vector";
import TileLayer from "ol/layer/Tile";
import VectorSource from "ol/source/Vector";
import Projection from "ol/proj/Projection";
import Static from "ol/source/ImageStatic";

import { Fill, RegularShape, Stroke, Style } from "ol/style";

var stroke = new Stroke({ color: "black", width: 2 });

var styles = {
    square: new Style({
        image: new RegularShape({
            stroke: stroke,
            points: 4,
            radius: 10,
            angle: Math.PI / 4,
        }),
    }),
};

var extent = [0, 0, 2048, 2048];

var styleKeys = "square";
var count = 50;
var features = new Array(count);
var e = 450000;
for (var i = 0; i < count; ++i) {
    var coordinates = [2 * e * Math.random() - e, 2 * e * Math.random() - e];
    features[i] = new Feature(new Point(coordinates));
    features[i].setStyle(styles[styleKeys]);
}

var source = new VectorSource({});

var vectorLayer = new VectorLayer({
    source: source,
});

var imageLayer = new ImageLayer({
    source: new Static({
        url: "https://imgs.xkcd.com/comics/online_communities.png",
        projection: new Projection({
            units: "pixels",
            extent: extent,
        }),
        imageExtent: extent,
    }),
});
vectorLayer.setZIndex(1);
imageLayer.setZIndex(0);

var olMap = new Map({
    target: null,
    layers: [imageLayer, vectorLayer],

    view: new View({
    //if i add projection the vector layer doesnt render

     projection: new Projection({
           units: "pixels",
              extent: extent,
           }),
        center: getCenter(extent),
    center: [0,0],
        zoom: 2,
    }),
});

class App extends Component {
    componentDidMount() {
    olMap.setTarget("olmap");
        vectorLayer.getSource().addFeatures(features);
    }

    render() {
        return (
            <div className="App">
                <div id="olmap" style={{ width: "100%", height: "80%" }} />
            </div>
        );
    }
}

Best Answer

What's important to understand here is relationship between projection of the view and projection of the layer shown in the view, when projections differ.

In the case of raster layer, OpenLayers does automatic on the fly reprojection of layer CRS to view CRS, but only if transformation is defined in projection definition, which is the case for all EPSG projections defined in proj4.

In the case of vector layer, there is no automatic reprojection, transformation has to be done programmatically, usually when reading the layer source.

In your case you have raster layer with simple Cartesian CRS and vector layer with randomly generated coordinates in the range of standard EPSG:3857. Since there is no transform between those two systems, you cannot put them in the same view. The main requirement to show both layers in the same view is to adjust/transform coordinates of both layers to the same extent.

If you want to use Cartesian pixel CRS in the view, then coordinates of your features have to be within extent of this projection, something like this:

var e = 2400;
for (var i = 0; i < count; ++i) {
  var coordinates = [e * Math.random(), e * Math.random()];
  features[i] = new Feature(new Point(coordinates));
  features[i].setStyle(styles[styleKeys]);
}

You can also decide to keep existing vector coordinates range. In this case you have to change image extent:

var e = 450000;
var extent = [-e, -e, e, e];

In this case you can also leave your view at standard EPSG:3857, just initial zoom has to be increased.

What's interesting here is that in this case e can be any number, it's size will just effect size of the map at given zoom. If e is image pixel dimension, then at zoom 0 image will be shown in it's natural size.

Related Question