aboutsummaryrefslogtreecommitdiff
path: root/src/PJ_minimal.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/PJ_minimal.c')
-rw-r--r--src/PJ_minimal.c204
1 files changed, 204 insertions, 0 deletions
diff --git a/src/PJ_minimal.c b/src/PJ_minimal.c
new file mode 100644
index 00000000..1108c4fa
--- /dev/null
+++ b/src/PJ_minimal.c
@@ -0,0 +1,204 @@
+/***********************************************************************
+
+ A minimal example of a new proj.4 projection implementation
+
+ ...and a verbose justification for some highly intrusive code
+ surgery
+
+************************************************************************
+
+**The brief version:**
+
+In an attempt to make proj.4 code slightly more secure and much easier
+to read and maintain, I'm trying to eliminate a few unfortunate design
+decisions from the early days of proj.4
+
+The work will be *very* intrusive, especially in the PJ_xxx segment of
+the code tree, but great care has been taken to design a process that
+can be implemented stepwise and localized, one projection at a time,
+then finalized with a relatively small and concentrated work package.
+
+**The (very) long version:**
+
+Gerald I. Evenden's original design for the proj.4 projection system
+is a beautiful example of software architecture, where a very limited
+set of policy rules leads to a well defined hierarchical structure and
+a high degree of both encapsulation and internal interoperability.
+
+In the proj.4 code, the policy rules are *enforced* by a system of
+preprocessor macros for building the scaffolding for implementation
+of a new projection.
+
+While this system of macros undeniably possesses the property of both
+reducing repetitive code and enforcing policy, unfortunately it also
+possesses two much less desirable properties:
+
+First, while enforcing policy, it also *hides* policy: The "beauty in
+simplicity" of Gerald's design is hidden behind layers of macros,
+whose architectural clarity do not match that of proj.4 in general.
+
+Second (and related), the macros make the source code look like
+something only vaguely related to C, making it hard to read (an effect
+that gets amplified to the tune of syntax highlighters getting confused
+by the macros).
+
+While the policy rule enforcement macros can be eliminated in relatively
+non-intrusive ways, a more fundamental flaw in the proj.4 use of macros
+is found in the PJ_xxx.c files implementing the individual projections:
+The use of internal redefinition of PJ, the fundamental proj data object,
+through the use of the PROJ_PARMS__ macro, makes the sizeof (PJ)
+fundamentally unknown to the calling pj_init function.
+
+This leads to code that is probably not in full conformance with the
+C standard.
+
+It is also a memory management catastrophe waiting to happen.
+
+But first and foremost, it leads to some very clumsy initialization code,
+where pj_init (the constructor function), needs to start the constsruction
+process by asking the PJ_xxx function to do the memory allocation (because
+pj_init does not know the size of the PROJ_PARMS-mangled PJ object being
+instantiated).
+
+Then, after doing some initialization work, pj_init returns control to
+PJ_xxx, asking it to finalize the initialization with the projection
+specific parameters specified by the PROJ_PARMS__ macro.
+
+Behind the scenes, hidden by two layers of macros, what happens is even
+worse, as a lot of the initialization code is duplicated in every PJ_xxx
+file, rather than being centralized in the pj_init function.
+
+**Solution procedure:**
+
+Evidently, the way to eliminate this clumsyness will be to introduce an
+opaque object, that is managed by tne individual PJ_xxx projection code,
+and represented as a simple void-pointer in the PJ object.
+
+This can be done one projection code file at a time, working through the
+code base as time permits (it will take at least a month).
+
+When a PJ_xxx file is on the surgical bench, it will also have its
+ENTRYA/ENTRY0/ENTRY1/ENTRY2/ENDENTRY/etc. etc. macro-guts torn out and
+replaced by the PROJECTION macro (introduced in projects.h).
+
+This leads to code that looks a lot more like real C, and hence is much
+less confusing to both syntax higlighters and humans. It also leads
+to code that, after all projections have been processed, with a final
+sweep over the code base can be brought into the style of the code in
+PJ_minimal.c
+
+In my humble opinion the result wil be a code base that is not only easier
+to maintain, but also more welcoming to new contributors.
+
+And if proj is to expand its strong basis in projections into the fields
+of geodetic transformations and general geometric geodesy, we will need
+to be able to attract quite a few expert geodesist contributors.
+
+And since expert geodesists are not necessarily expert coders, a welcoming
+code base is a real asset (to put the icing on the cake of the already
+welcoming user- and developer community).
+
+Note that the entire process does not touch the algorithmic/mathematical
+parts of the code at all - it is actuallly an attempt to make this part
+stand out more clearly.
+
+---
+
+The attached material is an attempt to show what happens if we remove
+the layers of macros, and introduce a more centralized approach to
+memory allocation and initialization.
+
+Please note, however, that the level of cantralization achieved here
+is not yet fully supported by the proj.4 infrastructure: It is an
+example, intended to show what can be achieved through a smooth,
+gradual and safe refactoring of the existing layered macro system.
+
+In my humble opinion, this version makes the beauty of Gerald's design
+much more evident than the current layered-macro-version.
+
+Thomas Knudsen, thokn@sdfe.dk, 2016-03-31
+
+***********************************************************************/
+
+#define PJ_LIB__
+#include <projects.h>
+#include <assert.h>
+PROJ_HEAD(minimal, "Minimal example (brief description goes here)");
+
+
+/* Projection specific elements for the PJ object */
+struct pj_opaque {
+ double a;
+ int b;
+};
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ /* Actual ellipsoidal forward code goes here */
+ xy.y = lp.lam + P->es;
+ xy.x = lp.phi + 42;
+ return xy;
+}
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ /* Actual spheroidal forward code goes here */
+ xy.y = lp.lam + P->es;
+ xy.x = lp.phi + 42;
+ return xy;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ /* Actual ellipsoidal forward code goes here */
+ lp.lam = xy.x - P->es;
+ lp.phi = xy.y - P->opaque->b;
+ return lp;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ /* Actual spheroidal forward code goes here */
+ lp.lam = xy.x - P->es;
+ lp.phi = xy.y - P->opaque->b;
+ return lp;
+}
+
+
+static void freeup(PJ *P) { /* Destructor */
+ if (P==0)
+ return;
+ /* Projection specific deallocation goes here */
+ pj_dealloc (P->opaque);
+ pj_dealloc (P);
+ return;
+}
+
+
+PJ *pj_projection_specific_setup_minimal (PJ *P) {
+ pj_prepare (P, des_minimal, freeup, sizeof (struct pj_opaque));
+ if (0==P->opaque) {
+ freeup (P);
+ return 0;
+ }
+
+ P->opaque->a = 42.42;
+ P->opaque->b = 42;
+
+ /* Spheroidal? */
+ if (0==P->es) {
+ P->fwd = s_forward;
+ P->inv = s_inverse;
+ return P;
+ }
+
+ /* Otherwise it's ellipsoidal */
+ P->fwd = e_forward;
+ P->inv = e_inverse;
+
+ return P;
+}