# How to overlay and scale a Plot onto an image of another plot

I am building a tool to help with pump selection for pipe systems. It is intended to have a database of pump curve images (which are determined experimentally by the manufacturers and published as images) along with manually-determined coordinates of the corners of the plot region, and overlay a pipe system curve on it to determine operating points, optimize efficiency, etc.

Simulate a pump curve image copied out of a manufacturer's catalog:

pumpcurve =  Rasterize[Plot[50 - (q/80)^(5/2), {q, 0, 300},
PlotRange -> {{0, 400}, {0, 60}}, Frame -> True,
FrameLabel -> {{"Total Head, ft", None}, {"Flowrate, gpm", None}},
PlotRangePadding -> None, ImageSize -> 500]]


Get Coordinates for lower left and upper right corners of frame using coordinate tool. On my system they are at {34,35} and {491,317}.

Create a pipe system curve and use Inset to overlay pipecurve on pumpcurve. The yellow background shows the full size of pipecurve. We can locate the lower left corner of the frame easily using the third argument of Inset, which works in the original plot coordinates. However the 4th argument, size, is the difficulty. It is in the coordinates of pumpcurve, and sizes the entire pipecurve, including ticks, axes labels, etc. around the Frame. What I would instead like to do is to size the plot region so that {400,60} in the pipecurve coordinates is at {491, 317} in the pumpcurve coordinates.

With[{
plotLLcorner = {34, 35},
plotURcorner = {491, 317}
},
Module[{pipecurve, size = plotURcorner - plotLLcorner},
pipecurve =
Plot[.001 q^2, {q, 0, 300}, PlotRange -> {{0, 400}, {0, 60}},
PlotStyle -> Red, Frame -> True, FrameStyle -> Blue,
PlotRangePadding -> None, AspectRatio -> Divide @@ Reverse[size]];
Show[
pumpcurve,
Graphics[{Background -> Directive[Opacity[.1], Yellow],
Inset[pipecurve, plotLLcorner, {0, 0}, size]}],
ImageSize -> 500
]
]]


A workaround is to remove everything around the frame, however I have a need to put some alternate scales on the pipecurve to display more information so this is not desirable. Another workaround is to give the user a Locator to manually scale the image, but this is also not desirable. I also tried DynamicLocation but can't get around a circular reference problem.

Is there any way to convert between ImageScaled and plot coordinates so that I could calculate a scale factor, or is there a better way to tackle this problem than using Inset?

• You should be able tocalculate the size from your plot range, the image dimension, and the distance between the two corners of the frame on the image: size = ImageDimensions@pipecurve / (plotLLcorner - plotURcorner) * Subtract@@@plotrange, where plotrange = {{0, 400}, {0, 60}}. This essentially scales the image such that the ratio between plot-range and inset size is the same as the ratio between the frame size on the image and the image size. – Lukas Lang Aug 16 '18 at 23:37
• @LukasLang Unfortunately this doesn't work because ImageDimensions includes the borders around the frame but plotrange is only within the frame. Also I think you swapped the / and * above. Now if I could somehow get the dimensions in plot coordinates, like you can with the Get Coordinates tool, then I would have the border dimensions and could subtract them out and scale it. – David Creech Aug 17 '18 at 14:36
• Could provide the raw image that you want to inset? This way, testing would be a lot more convenient – Lukas Lang Aug 17 '18 at 14:39
• I did above - the pumpcurve = Rasterize... creates the image. Thanks for your help – David Creech Aug 17 '18 at 14:41
• Sorry, I didn't realize that - thanks for pointing it out – Lukas Lang Aug 17 '18 at 15:09

The following code demonstrates how this can be achieved:

With[
{
plotLLcorner = {34, 35},
plotURcorner = {491, 317},
plotrange = {{0, 400}, {0, 60}}
},
Module[
{pipecurve, size = plotURcorner - plotLLcorner},
pipecurve = Plot[
.001 q^2,
{q, 0, 300},
PlotRange -> plotrange,
PlotStyle -> Red,
Frame -> True,
FrameStyle -> Blue,
AspectRatio -> Divide @@ Reverse[size]
];
Show[
pipecurve,
Prolog -> Inset[
pumpcurve,
plotrange[[All, 1]],
plotLLcorner,
ImageDimensions@pumpcurve/size*-Subtract @@@ plotrange
],
ImageSize -> 500
]
]
]


Note that the role of the plot and the inset have been flipped, so now the rasterized image is shown inside the inset (the reverse is also possible of course, but this seemed easier).

The main idea is this:

• Using the plot as the main graphics and putting the rasterized image into the inset means that the position and size of the inset are given in the same units as the plot range.
• We know how big the rasterized image is, and we know the coordinates of the frame corners.
• Using the lower left corner, we position the image appropriately (note how the two positions are flipped w.r.t. the question). The target position is the lower left corner of the plot range (here {0,0}), original position (i.e. position on the image) is plotLLcorner.
• To get the size of the inset, we use the following relation:

$$\underbrace{\frac{\text{size of inset}}{\text{size of plot range}}}_{\text{coordinates of plot}}=\underbrace{\frac{\text{size of image}}{\text{size of plot range on image}}}_{\text{coordinates of image (i.e. pixels)}}$$

• That works well once I change the order of the Graphics and pipecurve in Show and pull the Plot options into Show - note in your image the red curve disappeared. You want me to edit? – David Creech Aug 17 '18 at 17:46
• Thanks, I've somehow completely missed that. I've also changed the code to insert the Inset using Prolog - this way you get to keep all the styling options from your plot object while still having the inset in the back – Lukas Lang Aug 17 '18 at 18:10
• That does it, thanks! I accepted your answer – David Creech Aug 17 '18 at 19:08