.. _rfc85: ========================================================================= MS RFC 85 (proposal): Configurable axis order strategies ========================================================================= :Date: 2012-07-11 :Author: Edward Nash :Contact: e.nash at dvz-mv dot de :Last Edited: $Date$ :Status: Draft :Version: MapServer 6.4 :Id: $Id$ 1. Overview ----------- The requirement to support "swapped" axis order (lat,lon) for some EPSG codes and some OGC service versions leads to a complex matrix of possibilities where different notations of the same code are to be interpreted differently in different contexts, e.g. EPSG:4326 actually refers to a different definition in WMS 1.1.1 to in WMS 1.3.0. For the purposes of this document, we define two terms: - for "legacy" codes the axis order is always easting-first - i.e. x,y or lon,lat - for "strict" codes the axis order may sometimes be northing-first - i.e. lat,lon - if defined as such by the EPSG, although it too is usually easting-first Furthermore, xxxx refers to the code itself, * to any arbitrary intervening characters, [] to an optional string. Identifying which EPSG codes have a swapped axis order is straightforward and handled well in msIsAxisInverted(): this document deals purely with when the swapped axis order should be applied. Currently the bulk of the interpretation of EPSG codes is handled in msLoadProjectionString, which follows the following logic: - EPSG:xxxx is interpreted as a legacy code - urn:ogc:def:crs:EPSG:[*:]xxxx is interpreted as a strict code - urn:x-ogc:def:crs:EPSG:[*:]xxxx is interpreted as a strict code - http://www.opengis.net/def/crs/EPSG/xxxx is interpreted as a strict code Missing in this function but found in OGC specs are (not necessarily an exhaustive list): - http://www.opengis.net/gml/srs/epsg.xml#xxxx (WFS 1.0, WFS 1.1) - urn:EPSG:geographicCRS:xxxx (WFS 1.1, should be interpreted as a strict code) Additionally, the function msLoadProjectionStringEPSG interprets EPSG:xxxx as a strict code: this function is used in WMS 1.3 and WFS 1.1 (and possibly elsewhere). Currently the services use these functions as follows: - WMS <= 1.1.1 should work in legacy mode, always uses msLoadProjectionString - WMS 1.3.0 should work in strict mode and always uses msLoadProjectionStringEPSG - WFS 1.0.0 should work in legacy mode, always uses msLoadProjectionString, but only supports EPSG:xxxx - WFS 1.1.0 should work in strict mode, always uses msLoadProjectionStringEPSG, but only supports EPSG:xxxx, urn:ogc:def:crs:EPSG:[*:]xxxx and urn:EPSG:geographicCRS:xxxx notation, converting the latter of these to EPSG:xxxx before passing to msLoadProjectionStringEPSG The filter parsing doesn't use either of these functions, instead supporting either *:xxxx or *#xxxx (i.e. any common notation), but always in legacy mode (even for WFS 1.1.0). The EPSG code parsing can therefore be considered somewhat hacky, with services having to alter the given codes before they can use the standard functions or the standard functions are bypassed altogether. 2. Proposal ----------- I propose that (an) attribute(s) should be added to the map object which indicates the strategy that should in general be used for determining the axis order to use when intepreting an EPSG code. These attributes may then be passed as parameters to msLoadProjectionString which then deals with all necessary interpretation based on this strategy. Each request handler may set this strategy to match its requirements, from Mapscript and non-OGC requests a default strategy (possibly more-or-less matching the current logic) is used. It should also be possible to allow the strategy to be set in the Mapfile (and via Mapscript), potentially on a service or even service/version basis to allow fine-grained control. Advantages ========== - Really a single function for interpreting EPSG codes: cleaner and easier to maintain code - No need for hacky workarounds in individual service code to workaround the previously fixed rules - Simplifies future service version implementations Disadvantages ============== - Could introduce backward-compatibility problems with existing applications - but see details below as to how workarounds may be produced - Could lead to incompatibilities between service instances as each server may be configured differently. However, note that current behaviour is also not consistent across e.g. different WFS1.1 implementations: compare and contrast the Mapserver behaviour with e.g. - http://docs.geoserver.org/latest/en/user/services/wfs/basics.html - http://webhelp.esri.com/arcgisserver/9.3/java/wfs_service.htm#axis_order 3. Details ---------- Configuration ============= We add an optional key AXISORDER to the MAP object to define the default strategy. The value may be one of: - LEGACY for always using legacy axis order interpretation - STRICT for always using strict axis order interpretation - either LEGACY or STRICT followed by a list of prefixes which are to be interpreted as either LEGACY or STRICT: any other prefix will then be interpreted in the opposite way - DEFAULT for using whatever Mapserver considers the best option depending on what service is being used and trying to maintain more-or-less the current behaviour Optionally to allow per service/version configuration, we add to the WEB object metadata the key pattern AXISORDER_[_VERSION] with values as for the AXISORDER key in the MAP: this may be e.g. AXISORDER_WFS, AXISORDER_WFS_1_1_0, etc., with the service taking the most closely matching key to determine the strategy to use. If no key is present and the default strategy is DEFAULT then the service may set the strategy as is appropriate based on the standard and version in use (e.g. LEGACY for WMS <= 1.1.1, STRICT for WMS >= 1.3.0, etc.) Implementation ============== To the MAP object we add the following: :: char *axisorder_strategy char **axisorder_prefixes int axisorder_numprefixes These three parameters are also added to the msLoadProjectionString function: we do this rather than pass the map object to allow for cases where a particular call does not wish to use the strategy in the current map object and does not wish to change the strategy in the map object (e.g. when reading from a database such as PostGIS which always assumes legacy ordering or when acting as a WMS or WFS client for a server which displays different behaviour). A convenience function to set the strategy in the MAP object is provided: :: int msSetAxisOrderStrategy(mapobj *map, char *strategy, char **prefixes, int numprefixes) The msLoadProjectionString function is altered such that if the case-insensitive string "EPSG" appears in the string then the code itself is considered to be the part after the final : or #. The axis order is then determined in a separate step (extra function msRespectAxisOrder?) based on the strategy and whether the prefix matches one in the prefix list. The msLoadProjectionStringEPSG function is deleted. All places in the code where msLoadProjectionStringEPSG or msLoadProjectionString are called are altered appropriately. Other places in the code where EPSG strings are interpreted (e.g. in filter code) are also re-routed through msLoadProjectionString. Further functions must if necessary be modified such that either the map object or the three parameters defining the current axis-order strategy are available at the point the code is parsed. A quick look through the call hierarchy shows that in the majority of cases this is no problem as one of the map object or a mapserv or layer object are available in the calling function or its parent function. As an additional convenience to OWS service implementations we provide the following function: :: /* returns NULL if no specific strategy defined in WEB metadata */ const char* msOwsSpecificAxisOrderStrategy(mapobj *map, char *service, char *version, char **prefixes, int *numprefixes) Mapscript ========= The axisorder_ properties should be exposed in Mapscript, together with the msSetAxisOrderStrategy function. 4. Backwards compatibility issues --------------------------------- All existing configurations will continue to work, as they will implicitly use the DEFAULT strategy. Depending on how this default strategy is configured, there may be some slight changes in interpretation of code: this however should be considered a positive in that is an opportunity to clean up inconsistencies and potentially align Mapserver with other products and/or users expectations. If necessary then an example may be provided as to how the strategy may be configured to exactly match the current behaviour. Various functions will have their signature changed to enable the pass-through of the strategy parameters. This may break backward compatibility. 5. Affected files ----------------- - mapfile - mapwms - mapwfs - mapwfs11 - mapwcs - mapwcs11 - mapwcs20 - mapogcsos - mapogcfilter - mapogcfiltercommon - SWIG bindings - PHP Mapscript - others? 6. Possible extensions ---------------------- This document has mainly considered Mapserver as an OWS server, particularly in the configuration part. It may be desirable to allow users to configure the strategy to use when Mapserver is acting as a client on a per-server level in order to deal with differing behaviour from other servers. For such layers the metadata may be extended with keys similar to those proposed in the WEB object. 7. Open questions ----------------- What should the default strategy/strategies be? The current Mapserver behaviour, matching OGC reference implementations, strictly matching the standards, something else entirely? 8. Bug ID --------- * TBD 9. Voting history ----------------- No vote yet.