Opened 20 years ago

Last modified 20 years ago

#710 assigned defect

Scalefactor calculation logic not present in msDrawLayer function

Reported by: hrz@… Owned by: sdlime
Priority: high Milestone:
Component: MapServer C Library Version: 4.2
Severity: major Keywords:
Cc: sgillies@…

Description

The logic for calculating a layer's scalefactor is present in the msDrawMap 
function in the file mapdraw.c but not in the msDrawLayer function. This is a 
particular problem when using the mapscript API and the 
$layerObj->draw($imageObj) functionality in conjunction with a layer's SIZEUNITS 
or SYMBOLSCALE properties. The result in such a case is that msDrawMap (and the 
scaling logic) is bypassed and symbols are not scaled. 

The problem is further described in the submitted URL.

Our solution was to move the scalefactor calculation logic:

for(i=0;i<map->numlayers; i++) {
	if(map->layers[i].sizeunits != MS_PIXELS)
        map->layers[i].scalefactor = (msInchesPerUnit(map->layers[i].sizeunits,
0)/msInchesPerUnit(map->units,0)) / map->cellsize; 
	else if(map->layers[i].symbolscale > 0 && map->scale > 0)
        map->layers[i].scalefactor = map->layers[i].symbolscale/map->scale;
	else
        map->layers[i].scalefactor = 1;
}

down to the top of the msDrawLayer function:

if(layer->sizeunits != MS_PIXELS)
	 layer->scalefactor = (msInchesPerUnit(layer->sizeunits,0)
/msInchesPerUnit(map->units,0)) / map->cellsize; 
else if(layer->symbolscale > 0 && map->scale > 0)
	 layer->scalefactor = layer->symbolscale/map->scale;
else
	 layer->scalefactor = 1;

The result is that symbols are scaled correctly if layers are drawn 
individually.  This logic, however, is now missing from the other layer drawing 
functions:

msDrawWMSLayerLow
msDrawWMSLayerSWF
msDrawWMSLayerPDF

This bug is also present in version 4.0

Change History (7)

comment:1 by hrz@…, 20 years ago

I have just noticed that this bug is also present in the msDrawPoint function. 
Again this is particularly an issue when using $pointObj->draw in mapscript as 
the rendered points are not be scaled.

comment:2 by sdlime, 20 years ago

I know there's a reason for this, but I'll have to think about it for a 
minute...

comment:3 by sgillies@…, 20 years ago

Cc: sgillies@… added
Hey, I couldn't resist adding myself to this issue since I have been thinking
very hard about the mapscript drawing API.

Instead of adding more logic to the msDrawLayer* functions, I suggest that we
enhance the draw method of mapObj, making it:

  draw([imageObj image=NULL]) : imageObj

Have the draw method accept an optional imageObj argument.  If this argument
is provided, we draw on it and return the result.  Easy to implement and test
and only requires adding an imageObj argument to msDrawMap.

You'd take advantage of this enhancement like

  # Base image, might also come from prepareImage()
  image = mapscript.imageObj(200, 200, 'GD/PNG', 'example.png')
  
  # Turn off all map layers
  for i in range(the_map.numlayers):
      the_map.getLayer(i).status = mapscript.MS_OFF

  # Draw layer of interest
  the_map.getLayerByName('example').status = mapscript.MS_ON
  image = the_map.draw(image)

To make it even more user-friendly, maybe we give mapObj a new drawLayer()
method like

  drawLayer(layerObj layer [, imageObj image]) : image

so that you don't have to fiddle with status of all layers.

comment:4 by hrz@…, 20 years ago

Having thought about it a bit more it's clear to me that the problem stems from 
the fact that the layer scaling is being done in the wrong place; instead of 
occurring whenever something needs to be drawn it should be done whenever the 
scale changes.

This could be accomplished, for instance, by wrapping the msAdjustExtent and 
msCalculateScale functions in a function (e.g. msScaleMap/msRescale) which 
additionally sets the scalefactor for all the layers.

Regarding Sean's proposed mapscript API change, I can see how this would solve 
the layer scaling issue but what about the point drawing? Also it seems to me 
that conceptually any method dealing with drawing a specific layer should be 
associated with the layer object and not the map object.

comment:5 by hrz@…, 20 years ago

I forgot to mention in Comment #4 that the only place the msScaleMap/msRescale 
need be called is from within the mapscript zoom* and setextent functions...

comment:6 by sdlime, 20 years ago

Status: newassigned
The point drawing methods are pure convience. I got sick of wrapping points in 
shapeObj's. If one needed the full capabilities then shapeObj's would have to 
be used.

I don't like placing the scaling functionality in any of the zoom* functions. 
Too far away from the drawing, and those functions are not required to draw 
anything. Once the image is created, either with a draw or a prepare image 
seems the logical place, that was the plan anyway. That scalefactor should 
only have to be computed once per map.

Do you have a code snippet? I'm just curious of the sequence you're going 
through in MapScript.

Steve

comment:7 by sgillies@…, 20 years ago

Homme, we are already adjusting extent and re-calculating the map scale inside
mapObj.setExtent and mapObj.zoom* so that should be fine.

There is a related issue that when you resize a map through setting map.width
and map.height, the extents and scale are not adjusted.  My work-around has
been to call map.prepareImage after zooming or re-sizing.  This method has
some side-effects on the map that adjust it properly.

About the drawing API:
I disagree that the Layer is the most natural main noun in the drawing API.
A Layer is the only object which defines the symbols and styles used to render
the data.  These are like our brushes or pens (to use terms from other 
graphical software).  The Layer doesn't know anything about the size of our
mapping canvas and doesn't know anything about spatial extents or mapping
scale.

Also, in a well designed object oriented API, objects should modify only
themselves.  This principle leads me to conclude that the best drawing API to
aim for in the future has imageObj.drawLayer, imageObj.drawPoint, &c.  I'm
putting more thoughts about this in the mapserver wiki at

  http://mapserver.gis.umn.edu/cgi-bin/wiki.pl?RefactoringDrawingAPI
Note: See TracTickets for help on using tickets.