Multiple parameter lists for class definition

case class EarthMap(locationLL:WorldLocation=WorldLocation(lat= -90, lon= -180), // location on the globe in latitude;longitude
                    locationUR:WorldLocation=WorldLocation(lat=90, lon=179), // location on the globe in latitude;longitude
                    width:Int=360, height:Int=181, // size in pixels of the image file
                    borders:Boolean=true,
                    legend:Boolean=false,
                    grid:Option[(Double,Double)]=None,
                    projection:Projection = new SimpleProjection(locationLL.lon),
                    palette:ColorPalette = ColorPalette.defaultTemperatureColorPalette) {
...

I’d like to be able to reference an earlier initialization parameter from a later one.
Above is such an example, I’d like to use the call-site-provided locationLL parameter from the projection initialization value. This doesn’t work as is.

However, I accidentally discovered that I can create multiple parameter lists for the class definition, as follows, and it seems to work.

What I intend is that if the call-site does not specify locationLL, then locationLL.lon evaluates to -180.0. However, if the call-site specifies locationLL(lat=100.0,lon=45.0), then locationLL.lon evaluates to 45.0

Is this a robust solution, or will I regret it later? Of course I have to go an update all my call-sites where I instantiate the class.

case class EarthMap(locationLL:WorldLocation=WorldLocation(lat= -90, lon= -180), // location on the globe in latitude;longitude
                    locationUR:WorldLocation=WorldLocation(lat= 90, lon=179), // location on the globe in latitude;longitude
                    width:Int=360, height:Int=181, // size in pixels of the image file
                    borders:Boolean=true,
                    legend:Boolean=false,
                    grid:Option[(Double,Double)]=None
                   )( // *** NOTE, providing 2nd parameter list
                    projection:Projection = new SimpleProjection(locationLL.lon),
                    palette:ColorPalette = ColorPalette.defaultTemperatureColorPalette) {
...

Note that

  • The second param list is just an ordinary constructor param list. All the goodies that come with case classes won’t apply to its elements: No getter/setter, no inclusion in #copy(), #equals(), #hashCode(), #unapply(),…
  • You’ll always have to supply this list - if you want to get all the default values, it’d still have to be the empty list.

Alternatively, you could declare the argument as Option[Projection] and set the default in the constructor body if None (i.e. the default) is supplied. There may be more variants, like expecting the projection as an implicit argument, or refactoring your flat parameter list to “smarter” parameter objects that encode these default dependencies, but that’s dependent on the concrete case at hand, and I can’t judge whether these might me applicable to your setup.

Does this mean I won’t be able to use em.projection to extract the projection from an instance? I don’t need this at the moment, but If I did, I could write a getProjection method I suppose.

This is another case where I don’t yet know how the final model should be implemented. Part of me feels like the locationLL and locationUR should be part of the projection itself, but I’m not yet sure.

EarthMap represents an Image I want to create which displays a map, such as shown below.

The EarthMap object knows its Projection, and the Projection knows how to mathematically transform locations (longitutude,latitude) to and from the unit square.

A CylindiricalProjection has a cut line running from the north pole to the south pole. However, other projections may not a single cut line.

An EarthMap might have a zoom region. I.e., I might not want to draw the entire globe, but rather a bounding box specified by the LowerLeft and UpperRight corners given as longitude-latitude.

The limitation is that the EarthMap code does not know how to zoom to a region which spans the cut-line. So the cut-line must always be the left-hand-edge of the zoom bbox.

What I need to answer is: Is zooming to a view window part of projection or part of map drawing? For example if I’m using a like Authagraph, does the concept of zoom by longitude latitude still even make sense?

Sure, you could just declare the projection param as val, as in a vanilla class declaration, in order to have a getter. It’s just that I’d probably take a step back and check whether there’s more expressive ways of encoding this kind of dependency rather than through a half-case class and a flat list of params.

As for the domain-specific questions, I’m afraid I don’t know nothing about map projections, so I don’t have anything to contribute, other than perhaps: Maybe zooming is even an independent step in between projection and rendering, with a different (or amended), perhaps polymorphic, result representation?