camptocamp / inkmap

A library for generating high-quality, printable maps on the browser.
Other
86 stars 17 forks source link

Add style for geoJSON spec #44

Closed nboisteault closed 2 years ago

nboisteault commented 2 years ago

Hi @jahow, I have tried to make this PR work but the style stay as default when printing. I don't understand why, do you have any hint?

jahow commented 2 years ago

Ok so this change is actually a bit more involved: as the style is generated asynchronously, the createLayerGeoJSON function has to be asynchronous as well (which was not the case before). This also means that the progress$ observable needs to first emit an initial value of [0, null] (indicating that the layer generation started) and then [1, context.canvas] after the style was obtained and the layer drawn.

Untested code but it may look like this:

function createLayerGeoJSON(layerSpec, rootFrameState) {
  const width = rootFrameState.size[0];
  const height = rootFrameState.size[1];
  const context = createCanvasContext2D(width, height);
  // @ts-ignore
  context.canvas.style = {};

  const vectorSource = new VectorSource({
    features: new GeoJSON().readFeatures(layerSpec.geojson),
  });

  const layer = new VectorLayer({
    source: vectorSource,
  });

  let frameState = makeLayerFrameState(rootFrameState, layer);
  let renderer = layer.getRenderer();
  // @ts-ignore
  renderer.useContainer = useContainer.bind(renderer, context);

  // use a behaviour subject for the progress observable
  const progress$ = new BehaviorSubject([0, null]);

  // when this promise resolves, the layer is ready to be drawn
  const styleReadyPromise = layerSpec.style ? 
    new OpenLayersParser()
      .writeStyle(layerSpec.style)
      .then((olStyle) => layer.setStyle(olStyle))
      .catch((error) => console.log(error))
    : Promise.resolve();

  // when ready, draw layer & send a complete progress value
  styleReadyPromise.then(() => {
    renderer.prepareFrame({ ...frameState, time: Date.now() });
    renderer.renderFrame({ ...frameState, time: Date.now() }, context.canvas);
    progress$.next([1, context.canvas]);
    progress$.complete();
  });

  return progress$;
}

Even if this doesn't work as is, I hope it will make it clearer what I meant about making the function asynchronous. Thanks!

nboisteault commented 2 years ago

Perfect it works! Does this example is ok for the demo?

inkmap (30)

jahow commented 2 years ago

Looking good! Could you please also include a style in the rendering test for geojson layers?

nboisteault commented 2 years ago

@jahow ready to merge! Thanks for your help.

jahow commented 2 years ago

All good! I'll probably release a new version soon with this improvement, let me know if you intend to make other contributions that could be included as well. Thanks!

nboisteault commented 2 years ago

I might make other contributions when I'll work on printing in Lizmap but I'll do that by the end of the year or begin of 2022. I'd like a new release when possible. Thanks!