openseadragon / svg-overlay

An OpenSeadragon plugin that adds SVG overlay capability.
BSD 3-Clause "New" or "Revised" License
58 stars 28 forks source link

Way to use external svg file #28

Open vortice3D opened 6 years ago

vortice3D commented 6 years ago

Hi there Ian:

We're using your excellent pan-zoom framework in a development which manages assets in a side-by-side basis, using PNGs and SVGs. Of course, PNG's part is piece of cake, but I'm having problems in the SVG part.

Our SVGs are external files, wich we need to "ajax" in order to have the advantage of media-caching and the ability to tweak styles through CSS (not inline SVG or img translation are covered here).

This way We're "ajaxing" the SVG under the general algorithm to insert an external SVG in an HTML page:

var ajax=new XMLHttpRequest();
ajax.onload=function(e){
  try{
    document.body.insertBefore(ajax.responseXML.documentElement,document.body.childNodes[0]);
  }
  catch(e){
    console.log(e);
  }
};
//
ajax.open("GET",path,true);
ajax.responseType="document";
ajax.send();

The case is that under this approach I'm creating a node with all the header , symbols, etc..., and I suspect the overlay.node() SVG surface is waiting for SVG primitives only. So we had to parse our SVGs and add the different elements (path, rect, line, ...) one after the other. Isn´t it?

Best regards.

iangilman commented 6 years ago

Yes, this plugin creates its own SVG object:

https://github.com/openseadragon/svg-overlay/blob/master/openseadragon-svg-overlay.js#L34

One approach would be to pull the elements you want out of the SVG object you're loading via ajax and stuffing those into the SVG object the plugin creates. If you nest everything inside of a g, that could be quite straight forward.

Another option would be to modify this plugin so you can pass in an SVG element for it to use instead. The plugin is very small, so it should be an easy code change if you want to go that way, and I'd be happy to merge such a patch if you wanted to make a pull request.

vortice3D commented 6 years ago

Hi Ian:

Well I prefer to use the first approach , that is, to parse the SVG file pulling out the contents I need to draw.

Anyway, trying to implement that is not working: overlay.node().appendChild(xhr.responseXML.documentElement.getElementById("bird")); (where "bird" is the name of a svg path):

But following the same technique using a regular html svg element, (svgBlock) as container, instead overlay.node(), perfectly works: document.getElementById("svgBlock").appendChild(xhr.responseXML.documentElement.getElementById("bird"))

Any help on this?

Best regards.

vortice3D commented 6 years ago

Hi Ian:

I can see that when using the xhr.response directly to a svg block (not OSD at all), my code looks like this: <svg id="svgContent" class="openseadragon1"><path id="bird" d="M210.333,65.331C104.367,66.105-12.349,150.637,1.056,276.449c4.303,40.393,18.533,63.704,52.171,79.03 c36.307,16.544,57.022,54.556,50.406,112.954c-9.935,4.88-17.405,11.031-19.132,20.015c7.531-0.17,14.943-0.312,22.59,4.341 c20.333,12.375,31.296,27.363,42.979,51.72c1.714,3.572,8.192,2.849,8.312-3.078c0.17-8.467-1.856-17.454-5.226-26.933 c-2.955-8.313,3.059-7.985,6.917-6.106c6.399,3.115,16.334,9.43,30.39,13.098c5.392,1.407,5.995-3.877,5.224-6.991 c-1.864-7.522-11.009-10.862-24.519-19.229c-4.82-2.984-0.927-9.736,5.168-8.351l20.234,2.415c3.359,0.763,4.555-6.114,0.882-7.875 c-14.198-6.804-28.897-10.098-53.864-7.799c-11.617-29.265-29.811-61.617-15.674-81.681c12.639-17.938,31.216-20.74,39.147,43.489 c-5.002,3.107-11.215,5.031-11.332,13.024c7.201-2.845,11.207-1.399,14.791,0c17.912,6.998,35.462,21.826,52.982,37.309 c3.739,3.303,8.413-1.718,6.991-6.034c-2.138-6.494-8.053-10.659-14.791-20.016c-3.239-4.495,5.03-7.045,10.886-6.876 c13.849,0.396,22.886,8.268,35.177,11.218c4.483,1.076,9.741-1.964,6.917-6.917c-3.472-6.085-13.015-9.124-19.18-13.413 c-4.357-3.029-3.025-7.132,2.697-6.602c3.905,0.361,8.478,2.271,13.908,1.767c9.946-0.925,7.717-7.169-0.883-9.566 c-19.036-5.304-39.891-6.311-61.665-5.225c-43.837-8.358-31.554-84.887,0-90.363c29.571-5.132,62.966-13.339,99.928-32.156 c32.668-5.429,64.835-12.446,92.939-33.85c48.106-14.469,111.903,16.113,204.241,149.695c3.926,5.681,15.819,9.94,9.524-6.351 c-15.893-41.125-68.176-93.328-92.13-132.085c-24.581-39.774-14.34-61.243-39.957-91.247 c-21.326-24.978-47.502-25.803-77.339-17.365c-23.461,6.634-39.234-7.117-52.98-31.273C318.42,87.525,265.838,64.927,210.333,65.331 z M445.731,203.01c6.12,0,11.112,4.919,11.112,11.038c0,6.119-4.994,11.111-11.112,11.111s-11.038-4.994-11.038-11.111 C434.693,207.929,439.613,203.01,445.731,203.01z"></path></svg>

And when I load it on the OSD overlay (overlay.node()), it is the following: <svg style="position: absolute; left: 0px; top: 0px; width: 100%; height: 100%;" width="1222" height="982"><g transform="translate(-371.94953627932824,-119.01136570285395) scale(1837.0000000000002) rotate(0)"><path id="bird" d="M210.333,65.331C104.367,66.105-12.349,150.637,1.056,276.449c4.303,40.393,18.533,63.704,52.171,79.03 c36.307,16.544,57.022,54.556,50.406,112.954c-9.935,4.88-17.405,11.031-19.132,20.015c7.531-0.17,14.943-0.312,22.59,4.341 c20.333,12.375,31.296,27.363,42.979,51.72c1.714,3.572,8.192,2.849,8.312-3.078c0.17-8.467-1.856-17.454-5.226-26.933 c-2.955-8.313,3.059-7.985,6.917-6.106c6.399,3.115,16.334,9.43,30.39,13.098c5.392,1.407,5.995-3.877,5.224-6.991 c-1.864-7.522-11.009-10.862-24.519-19.229c-4.82-2.984-0.927-9.736,5.168-8.351l20.234,2.415c3.359,0.763,4.555-6.114,0.882-7.875 c-14.198-6.804-28.897-10.098-53.864-7.799c-11.617-29.265-29.811-61.617-15.674-81.681c12.639-17.938,31.216-20.74,39.147,43.489 c-5.002,3.107-11.215,5.031-11.332,13.024c7.201-2.845,11.207-1.399,14.791,0c17.912,6.998,35.462,21.826,52.982,37.309 c3.739,3.303,8.413-1.718,6.991-6.034c-2.138-6.494-8.053-10.659-14.791-20.016c-3.239-4.495,5.03-7.045,10.886-6.876 c13.849,0.396,22.886,8.268,35.177,11.218c4.483,1.076,9.741-1.964,6.917-6.917c-3.472-6.085-13.015-9.124-19.18-13.413 c-4.357-3.029-3.025-7.132,2.697-6.602c3.905,0.361,8.478,2.271,13.908,1.767c9.946-0.925,7.717-7.169-0.883-9.566 c-19.036-5.304-39.891-6.311-61.665-5.225c-43.837-8.358-31.554-84.887,0-90.363c29.571-5.132,62.966-13.339,99.928-32.156 c32.668-5.429,64.835-12.446,92.939-33.85c48.106-14.469,111.903,16.113,204.241,149.695c3.926,5.681,15.819,9.94,9.524-6.351 c-15.893-41.125-68.176-93.328-92.13-132.085c-24.581-39.774-14.34-61.243-39.957-91.247 c-21.326-24.978-47.502-25.803-77.339-17.365c-23.461,6.634-39.234-7.117-52.98-31.273C318.42,87.525,265.838,64.927,210.333,65.331 z M445.731,203.01c6.12,0,11.112,4.919,11.112,11.038c0,6.119-4.994,11.111-11.112,11.111s-11.038-4.994-11.038-11.111 C434.693,207.929,439.613,203.01,445.731,203.01z"></path></g></svg>

So basically, the "bird" is there but is scaled or positioned in a way it can´t be seen over the OSD canvas.

The case is that the transform "figures" (translate(-371.94953627932824,-119.01136570285395) scale(1837.0000000000002) rotate(0)) seem to be correct. However, deleting that transform node makes the trick, and the bird is shown then.

As that transform node is added by OSD, and I suppose it is the basis for the zoom-pan functionality, how can I get rid of this behavior?

iangilman commented 6 years ago

I see! Yes, that transform is what aligns the SVG with the zoom/pan of the OSD object. To be precise, what it does is set your SVG to the viewport coordinate system of your OSD. If your SVG path isn't naturally in those viewport coordinates (because it's too big or too small), you have a couple of options: you can change your OSD viewport coordinate system, or you can scale your path.

To change the OSD viewport coordinates, you specify a different size for your image. By default the image is 1 unit wide. If you wanted 1000 units wide, you could do this:

OpenSeadragon({
  tileSources: [
    {
      tileSource: '/foo.dzi',
      width: 1000
    }
  ]
});

On the other hand, if you want to transform your path instead, you can add a transform attribute to it so it matches viewport coordinates. Then the transform in its parent group will handle the zooming/panning as normal.

You might even use a combination of the two techniques, changing the OSD coordinates to be a more comfortable scale, but also putting a translate transform on your path to make sure it is positioned properly.

BTW, for more info on viewport coordinates see:

http://openseadragon.github.io/examples/viewport-coordinates/

vortice3D commented 6 years ago

Well, I'm here again with the OSD part (among many) of this project.

First of all, thanks for your explanation!, Ian.

In order to keep the things simple and efficient, I'm trying to keep the same OSV Viewer settings, meanwhile toggling between the PNG (simple image, not pyramidal one) and the SVG visualization.

Said this, I assume the best solution is the second you have mentioned, as It can be done with not programming at all, in the SVG authoring stage of the workflow.

Said this, I'd like also to check the "tilesource width solution". Before going with the SVG part, I need to check whether changing the things, as you are instruct me, breaks my code down or not for the PNG part. Sadly, I've tried to change the width of the image without luck.

The case is that I implement "creation plus photo loading" in a two-stages pattern.

First:

function _create() {
                ...
        _viewer = OpenSeadragon({
            homeFillsViewer : true,
            id : param.viewer,
            navigatorId : param.navigator,
            showNavigationControl : false,
            showNavigator : true
        });
        ...
    }

and then, when needed:

function _loadImage(file) {
        ...
        _file = file;
        //
        var type = file.substring(file.length - 3, file.length);
        //fichero PNG (photo)
        if (type == "png") {
            _viewer.open({
                buildPyramid : false,
                type : "image",
                url : file
            });
        }
        //fichero SVG (blueprint)
        else if (type == "svg") {
                      //TO DO
                }
        ...
}

Said this, where must it be the "width" property set, in OSD constructor (stage 1) or when actually opening the file (stage 2)? As said, I've tried both approach without any luck.

Thank you for your time.

P.S. I'm sorry for the lack of indentation in the code blocks. It is there meanwhile writing but not shown in preview (?)

iangilman commented 6 years ago

To make your viewer width, say, 1000, you could do:

_viewer.open({
  tileSource: {
    buildPyramid : false,
    type : "image",
    url : file
  },
  width: 1000
});

This can work in the OSD constructor as well, if that's when you're adding the image... basically you do it wherever you add the image.

I've gone ahead and fixed the indentation in your comment... you just use ```.

vortice3D commented 6 years ago

Thanks again Ian.

I'll check it this weekend.