Subversion Repositories eFlore/Projets.eflore-projets

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1001 delphine 1
<?php
2
/**
3
 * Author : Julien Moquet
4
 *
5
 * Simple conversion from javascript to PHP of Proj4php by Mike Adair madairATdmsolutions.ca and Richard Greenwood rich@greenwoodmap.com
6
 *
7
 * License: LGPL as per: http://www.gnu.org/copyleft/lesser.html
8
 */
9
$dir = dirname( __FILE__ );
10
 
11
require_once($dir . "/proj4phpProj.php");
12
require_once($dir . "/proj4phpCommon.php");
13
require_once($dir . "/proj4phpDatum.php");
14
require_once($dir . "/proj4phpLongLat.php");
15
require_once($dir . "/proj4phpPoint.php");
16
 
17
class Proj4php {
18
 
19
    protected $defaultDatum = 'WGS84';
20
    public static $ellipsoid = array( );
21
    public static $common = null;
22
    public static $datum = array( );
23
    public static $defs = array( );
24
    public static $wktProjections = array( );
25
    public static $WGS84 = null;
26
    public static $primeMeridian = array( );
27
    public static $proj = array( );
28
 
29
    /**
30
     * Property: defsLookupService
31
     * service to retreive projection definition parameters from
32
     */
33
    public static $defsLookupService = 'http://spatialreference.org/ref';
34
 
35
    /**
36
      Proj4php.defs is a collection of coordinate system definition objects in the
37
      PROJ.4 command line format.
38
      Generally a def is added by means of a separate .js file for example:
39
 
40
      <SCRIPT type="text/javascript" src="defs/EPSG26912.js"></SCRIPT>
41
 
42
      def is a CS definition in PROJ.4 WKT format, for example:
43
      +proj="tmerc"   //longlat, etc.
44
      +a=majorRadius
45
      +b=minorRadius
46
      +lat0=somenumber
47
      +long=somenumber
48
     */
49
    protected function initDefs() {
50
        // These are so widely used, we'll go ahead and throw them in
51
        // without requiring a separate .js file
52
        self::$defs['WGS84'] = "+title=long/lat:WGS84 +proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees";
53
        self::$defs['EPSG:4326'] = "+title=long/lat:WGS84 +proj=longlat +a=6378137.0 +b=6356752.31424518 +ellps=WGS84 +datum=WGS84 +units=degrees";
54
        self::$defs['EPSG:4269'] = "+title=long/lat:NAD83 +proj=longlat +a=6378137.0 +b=6356752.31414036 +ellps=GRS80 +datum=NAD83 +units=degrees";
55
        self::$defs['EPSG:3875'] = "+title= Google Mercator +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs";
56
        self::$defs['EPSG:3785'] = self::$defs['EPSG:3875'];
57
        self::$defs['GOOGLE']    = self::$defs['EPSG:3875'];
58
        self::$defs['EPSG:900913'] = self::$defs['EPSG:3875'];
59
        self::$defs['EPSG:102113'] = self::$defs['EPSG:3875'];
60
    }
61
 
62
    //lookup table to go from the projection name in WKT to the Proj4php projection name
63
    //build this out as required
64
    protected function initWKTProjections() {
65
        self::$wktProjections["Lambert Tangential Conformal Conic Projection"] = "lcc";
66
        self::$wktProjections["Mercator"] = "merc";
67
        self::$wktProjections["Mercator_1SP"] = "merc";
68
        self::$wktProjections["Transverse_Mercator"] = "tmerc";
69
        self::$wktProjections["Transverse Mercator"] = "tmerc";
70
        self::$wktProjections["Lambert Azimuthal Equal Area"] = "laea";
71
        self::$wktProjections["Universal Transverse Mercator System"] = "utm";
72
    }
73
 
74
    protected function initDatum() {
75
        self::$datum["WGS84"] = array( 'towgs84' => "0,0,0", 'ellipse' => "WGS84", 'datumName' => "WGS84" );
76
        self::$datum["GGRS87"] = array( 'towgs84' => "-199.87,74.79,246.62", 'ellipse' => "GRS80", 'datumName' => "Greek_Geodetic_Reference_System_1987" );
77
        self::$datum["NAD83"] = array( 'towgs84' => "0,0,0", 'ellipse' => "GRS80", 'datumName' => "North_American_Datum_1983" );
78
        self::$datum["NAD27"] = array( 'nadgrids' => "@conus,@alaska,@ntv2_0.gsb,@ntv1_can.dat", 'ellipse' => "clrk66", 'datumName' => "North_American_Datum_1927" );
79
        self::$datum["potsdam"] = array( 'towgs84' => "606.0,23.0,413.0", 'ellipse' => "bessel", 'datumName' => "Potsdam Rauenberg 1950 DHDN" );
80
        self::$datum["carthage"] = array( 'towgs84' => "-263.0,6.0,431.0", 'ellipse' => "clark80", 'datumName' => "Carthage 1934 Tunisia" );
81
        self::$datum["hermannskogel"] = array( 'towgs84' => "653.0,-212.0,449.0", 'ellipse' => "bessel", 'datumName' => "Hermannskogel" );
82
        self::$datum["ire65"] = array( 'towgs84' => "482.530,-130.596,564.557,-1.042,-0.214,-0.631,8.15", 'ellipse' => "mod_airy", 'datumName' => "Ireland 1965" );
83
        self::$datum["nzgd49"] = array( 'towgs84' => "59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993", 'ellipse' => "intl", 'datumName' => "New Zealand Geodetic Datum 1949" );
84
        self::$datum["OSGB36"] = array( 'towgs84' => "446.448,-125.157,542.060,0.1502,0.2470,0.8421,-20.4894", 'ellipse' => "airy", 'datumName' => "Airy 1830" );
85
    }
86
 
87
    protected function initEllipsoid() {
88
        self::$ellipsoid["MERIT"] = array( 'a' => 6378137.0, 'rf' => 298.257, 'ellipseName' => "MERIT 1983" );
89
        self::$ellipsoid["SGS85"] = array( 'a' => 6378136.0, 'rf' => 298.257, 'ellipseName' => "Soviet Geodetic System 85" );
90
        self::$ellipsoid["GRS80"] = array( 'a' => 6378137.0, 'rf' => 298.257222101, 'ellipseName' => "GRS 1980(IUGG, 1980)" );
91
        self::$ellipsoid["IAU76"] = array( 'a' => 6378140.0, 'rf' => 298.257, 'ellipseName' => "IAU 1976" );
92
        self::$ellipsoid["airy"] = array( 'a' => 6377563.396, 'b' => 6356256.910, 'ellipseName' => "Airy 1830" );
93
        self::$ellipsoid["APL4."] = array( 'a' => 6378137, 'rf' => 298.25, 'ellipseName' => "Appl. Physics. 1965" );
94
        self::$ellipsoid["NWL9D"] = array( 'a' => 6378145.0, 'rf' => 298.25, 'ellipseName' => "Naval Weapons Lab., 1965" );
95
        self::$ellipsoid["mod_airy"] = array( 'a' => 6377340.189, 'b' => 6356034.446, 'ellipseName' => "Modified Airy" );
96
        self::$ellipsoid["andrae"] = array( 'a' => 6377104.43, 'rf' => 300.0, 'ellipseName' => "Andrae 1876 (Den., Iclnd.)" );
97
        self::$ellipsoid["aust_SA"] = array( 'a' => 6378160.0, 'rf' => 298.25, 'ellipseName' => "Australian Natl & S. Amer. 1969" );
98
        self::$ellipsoid["GRS67"] = array( 'a' => 6378160.0, 'rf' => 298.2471674270, 'ellipseName' => "GRS 67(IUGG 1967)" );
99
        self::$ellipsoid["bessel"] = array( 'a' => 6377397.155, 'rf' => 299.1528128, 'ellipseName' => "Bessel 1841" );
100
        self::$ellipsoid["bess_nam"] = array( 'a' => 6377483.865, 'rf' => 299.1528128, 'ellipseName' => "Bessel 1841 (Namibia)" );
101
        self::$ellipsoid["clrk66"] = array( 'a' => 6378206.4, 'b' => 6356583.8, 'ellipseName' => "Clarke 1866" );
102
        self::$ellipsoid["clrk80"] = array( 'a' => 6378249.145, 'rf' => 293.4663, 'ellipseName' => "Clarke 1880 mod." );
103
        self::$ellipsoid["CPM"] = array( 'a' => 6375738.7, 'rf' => 334.29, 'ellipseName' => "Comm. des Poids et Mesures 1799" );
104
        self::$ellipsoid["delmbr"] = array( 'a' => 6376428.0, 'rf' => 311.5, 'ellipseName' => "Delambre 1810 (Belgium)" );
105
        self::$ellipsoid["engelis"] = array( 'a' => 6378136.05, 'rf' => 298.2566, 'ellipseName' => "Engelis 1985" );
106
        self::$ellipsoid["evrst30"] = array( 'a' => 6377276.345, 'rf' => 300.8017, 'ellipseName' => "Everest 1830" );
107
        self::$ellipsoid["evrst48"] = array( 'a' => 6377304.063, 'rf' => 300.8017, 'ellipseName' => "Everest 1948" );
108
        self::$ellipsoid["evrst56"] = array( 'a' => 6377301.243, 'rf' => 300.8017, 'ellipseName' => "Everest 1956" );
109
        self::$ellipsoid["evrst69"] = array( 'a' => 6377295.664, 'rf' => 300.8017, 'ellipseName' => "Everest 1969" );
110
        self::$ellipsoid["evrstSS"] = array( 'a' => 6377298.556, 'rf' => 300.8017, 'ellipseName' => "Everest (Sabah & Sarawak)" );
111
        self::$ellipsoid["fschr60"] = array( 'a' => 6378166.0, 'rf' => 298.3, 'ellipseName' => "Fischer (Mercury Datum) 1960" );
112
        self::$ellipsoid["fschr60m"] = array( 'a' => 6378155.0, 'rf' => 298.3, 'ellipseName' => "Fischer 1960" );
113
        self::$ellipsoid["fschr68"] = array( 'a' => 6378150.0, 'rf' => 298.3, 'ellipseName' => "Fischer 1968" );
114
        self::$ellipsoid["helmert"] = array( 'a' => 6378200.0, 'rf' => 298.3, 'ellipseName' => "Helmert 1906" );
115
        self::$ellipsoid["hough"] = array( 'a' => 6378270.0, 'rf' => 297.0, 'ellipseName' => "Hough" );
116
        self::$ellipsoid["intl"] = array( 'a' => 6378388.0, 'rf' => 297.0, 'ellipseName' => "International 1909 (Hayford)" );
117
        self::$ellipsoid["kaula"] = array( 'a' => 6378163.0, 'rf' => 298.24, 'ellipseName' => "Kaula 1961" );
118
        self::$ellipsoid["lerch"] = array( 'a' => 6378139.0, 'rf' => 298.257, 'ellipseName' => "Lerch 1979" );
119
        self::$ellipsoid["mprts"] = array( 'a' => 6397300.0, 'rf' => 191.0, 'ellipseName' => "Maupertius 1738" );
120
        self::$ellipsoid["new_intl"] = array( 'a' => 6378157.5, 'b' => 6356772.2, 'ellipseName' => "New International 1967" );
121
        self::$ellipsoid["plessis"] = array( 'a' => 6376523.0, 'rf' => 6355863.0, 'ellipseName' => "Plessis 1817 (France)" );
122
        self::$ellipsoid["krass"] = array( 'a' => 6378245.0, 'rf' => 298.3, 'ellipseName' => "Krassovsky, 1942" );
123
        self::$ellipsoid["SEasia"] = array( 'a' => 6378155.0, 'b' => 6356773.3205, 'ellipseName' => "Southeast Asia" );
124
        self::$ellipsoid["walbeck"] = array( 'a' => 6376896.0, 'b' => 6355834.8467, 'ellipseName' => "Walbeck" );
125
        self::$ellipsoid["WGS60"] = array( 'a' => 6378165.0, 'rf' => 298.3, 'ellipseName' => "WGS 60" );
126
        self::$ellipsoid["WGS66"] = array( 'a' => 6378145.0, 'rf' => 298.25, 'ellipseName' => "WGS 66" );
127
        self::$ellipsoid["WGS72"] = array( 'a' => 6378135.0, 'rf' => 298.26, 'ellipseName' => "WGS 72" );
128
        self::$ellipsoid["WGS84"] = array( 'a' => 6378137.0, 'rf' => 298.257223563, 'ellipseName' => "WGS 84" );
129
        self::$ellipsoid["sphere"] = array( 'a' => 6370997.0, 'b' => 6370997.0, 'ellipseName' => "Normal Sphere (r=6370997)" );
130
    }
131
 
132
    protected function initPrimeMeridian() {
133
        self::$primeMeridian["greenwich"] = '0.0';               //"0dE",
134
        self::$primeMeridian["lisbon"] = -9.131906111111;   //"9d07'54.862\"W",
135
        self::$primeMeridian["paris"] = 2.337229166667;   //"2d20'14.025\"E",
136
        self::$primeMeridian["bogota"] = -74.080916666667;  //"74d04'51.3\"W",
137
        self::$primeMeridian["madrid"] = -3.687938888889;  //"3d41'16.58\"W",
138
        self::$primeMeridian["rome"] = 12.452333333333;  //"12d27'8.4\"E",
139
        self::$primeMeridian["bern"] = 7.439583333333;  //"7d26'22.5\"E",
140
        self::$primeMeridian["jakarta"] = 106.807719444444;  //"106d48'27.79\"E",
141
        self::$primeMeridian["ferro"] = -17.666666666667;  //"17d40'W",
142
        self::$primeMeridian["brussels"] = 4.367975;        //"4d22'4.71\"E",
143
        self::$primeMeridian["stockholm"] = 18.058277777778;  //"18d3'29.8\"E",
144
        self::$primeMeridian["athens"] = 23.7163375;       //"23d42'58.815\"E",
145
        self::$primeMeridian["oslo"] = 10.722916666667;  //"10d43'22.5\"E"
146
    }
147
 
148
    /**
149
     *
150
     */
151
    public function __construct() {
152
 
153
        $this->initWKTProjections();
154
        $this->initDefs();
155
        $this->initDatum();
156
        $this->initEllipsoid();
157
        $this->initPrimeMeridian();
158
 
159
        self::$proj['longlat'] = new proj4phpLongLat();
160
        self::$proj['identity'] = new proj4phpLongLat();
161
        self::$common = new proj4phpCommon();
162
        self::$WGS84 = new Proj4phpProj( 'WGS84' );
163
    }
164
 
165
    /**
166
     * Method: transform(source, dest, point)
167
     * Transform a point coordinate from one map projection to another.  This is
168
     * really the only public method you should need to use.
169
     *
170
     * Parameters:
171
     * source - {Proj4phpProj} source map projection for the transformation
172
     * dest - {Proj4phpProj} destination map projection for the transformation
173
     * point - {Object} point to transform, may be geodetic (long, lat) or
174
     *     projected Cartesian (x,y), but should always have x,y properties.
175
     */
176
    public function transform( $source, $dest, $point ) {
177
 
178
        if( !$source->readyToUse ) {
179
            self::reportError( "Proj4php initialization for:" . $source->srsCode . " not yet complete" );
180
            return $point;
181
        }
182
        if( !$dest->readyToUse ) {
183
            self::reportError( "Proj4php initialization for:" . $dest->srsCode . " not yet complete" );
184
            return $point;
185
        }
186
 
187
        // Workaround for datum shifts towgs84, if either source or destination projection is not wgs84
188
        if ( isset($source->datum) && isset($dest->datum) && (
189
            (($source->datum->datum_type == Proj4php::$common->PJD_3PARAM || $source->datum->datum_type == Proj4php::$common->PJD_7PARAM) && (isset($dest->datumCode) && $dest->datumCode != "WGS84")) ||
190
            (($dest->datum->datum_type == Proj4php::$common->PJD_3PARAM || $dest->datum->datum_type == Proj4php::$common->PJD_7PARAM) && (isset($source->datumCode) && $source->datumCode != "WGS84")))) {
191
            $wgs84 = Proj4php::$WGS84;
192
            $this->transform($source, $wgs84, $point);
193
            $source = $wgs84;
194
        }
195
 
196
        // Workaround for Spherical Mercator => skipped in proj4js 1.1.0
197
        /*
198
        if( ($source->srsProjNumber == "900913" && $dest->datumCode != "WGS84") ||
199
            ($dest->srsProjNumber == "900913" && $source->datumCode != "WGS84") ) {
200
            $wgs84 = Proj4php::$WGS84; // DONT KNOW WHAT YET
201
            $this->transform( $source, $wgs84, $point );
202
            $source = $wgs84;
203
        }
204
        */
205
 
206
        // DGR, 2010/11/12
207
        if( $source->axis != "enu" ) {
208
            $this->adjust_axis( $source, false, $point );
209
        }
210
 
211
        // Transform source points to long/lat, if they aren't already.
212
        if( $source->projName == "longlat" ) {
213
            $point->x *= Proj4php::$common->D2R;  // convert degrees to radians
214
            $point->y *= Proj4php::$common->D2R;
215
        } else {
216
            if( isset($source->to_meter) ) {
217
                $point->x *= $source->to_meter;
218
                $point->y *= $source->to_meter;
219
            }
220
            $source->inverse( $point ); // Convert Cartesian to longlat
221
        }
222
 
223
        // Adjust for the prime meridian if necessary
224
        if( isset( $source->from_greenwich ) ) {
225
            $point->x += $source->from_greenwich;
226
        }
227
 
228
        // Convert datums if needed, and if possible.
229
        $point = $this->datum_transform( $source->datum, $dest->datum, $point );
230
 
231
        // Adjust for the prime meridian if necessary
232
        if( isset( $dest->from_greenwich ) ) {
233
            $point->x -= $dest->from_greenwich;
234
        }
235
 
236
        if( $dest->projName == "longlat" ) {
237
            // convert radians to decimal degrees
238
            $point->x *= Proj4php::$common->R2D;
239
            $point->y *= Proj4php::$common->R2D;
240
        } else {               // else project
241
            $dest->forward( $point );
242
            if( isset($dest->to_meter) ) {
243
                $point->x /= $dest->to_meter;
244
                $point->y /= $dest->to_meter;
245
            }
246
        }
247
 
248
        // DGR, 2010/11/12
249
        if( $dest->axis != "enu" ) {
250
            $this->adjust_axis( $dest, true, $point );
251
        }
252
 
253
        return $point;
254
    }
255
 
256
 
257
    /** datum_transform()
258
      source coordinate system definition,
259
      destination coordinate system definition,
260
      point to transform in geodetic coordinates (long, lat, height)
261
     */
262
    public function datum_transform( $source, $dest, $point ) {
263
 
264
        // Short cut if the datums are identical.
265
        if( $source->compare_datums( $dest ) ) {
266
            return $point; // in this case, zero is sucess,
267
            // whereas cs_compare_datums returns 1 to indicate TRUE
268
            // confusing, should fix this
269
        }
270
 
271
        // Explicitly skip datum transform by setting 'datum=none' as parameter for either source or dest
272
        if( $source->datum_type == Proj4php::$common->PJD_NODATUM
273
            || $dest->datum_type == Proj4php::$common->PJD_NODATUM ) {
274
            return $point;
275
        }
276
 
277
        /*
278
        // If this datum requires grid shifts, then apply it to geodetic coordinates.
279
        if( $source->datum_type == Proj4php::$common->PJD_GRIDSHIFT ) {
280
            throw(new Exception( "ERROR: Grid shift transformations are not implemented yet." ));
281
        }
282
 
283
        if( $dest->datum_type == Proj4php::$common->PJD_GRIDSHIFT ) {
284
            throw(new Exception( "ERROR: Grid shift transformations are not implemented yet." ));
285
        }
286
        */
287
 
288
        // Do we need to go through geocentric coordinates?
289
        if( $source->es != $dest->es || $source->a != $dest->a
290
            || $source->datum_type == Proj4php::$common->PJD_3PARAM
291
            || $source->datum_type == Proj4php::$common->PJD_7PARAM
292
            || $dest->datum_type == Proj4php::$common->PJD_3PARAM
293
            || $dest->datum_type == Proj4php::$common->PJD_7PARAM ) {
294
 
295
            // Convert to geocentric coordinates.
296
            $source->geodetic_to_geocentric( $point );
297
            // CHECK_RETURN;
298
            // Convert between datums
299
            if( $source->datum_type == Proj4php::$common->PJD_3PARAM || $source->datum_type == Proj4php::$common->PJD_7PARAM ) {
300
                $source->geocentric_to_wgs84( $point );
301
                // CHECK_RETURN;
302
            }
303
 
304
            if( $dest->datum_type == Proj4php::$common->PJD_3PARAM || $dest->datum_type == Proj4php::$common->PJD_7PARAM ) {
305
                $dest->geocentric_from_wgs84( $point );
306
                // CHECK_RETURN;
307
            }
308
 
309
            // Convert back to geodetic coordinates
310
            $dest->geocentric_to_geodetic( $point );
311
            // CHECK_RETURN;
312
        }
313
 
314
        // Apply grid shift to destination if required
315
        /*
316
        if( $dest->datum_type == Proj4php::$common->PJD_GRIDSHIFT ) {
317
            throw(new Exception( "ERROR: Grid shift transformations are not implemented yet." ));
318
            // pj_apply_gridshift( pj_param(dest.params,"snadgrids").s, 1, point);
319
            // CHECK_RETURN;
320
        }
321
        */
322
        return $point;
323
    }
324
 
325
 
326
    /**
327
     * Function: adjust_axis
328
     * Normalize or de-normalized the x/y/z axes.  The normal form is "enu"
329
     * (easting, northing, up).
330
     * Parameters:
331
     * crs {Proj4php.Proj} the coordinate reference system
332
     * denorm {Boolean} when false, normalize
333
     * point {Object} the coordinates to adjust
334
     */
335
    public function adjust_axis( $crs, $denorm, $point ) {
336
 
337
        $xin = $point->x;
338
        $yin = $point->y;
339
        $zin = isset( $point->z ) ? $point->z : 0.0;
340
        #$v;
341
        #$t;
342
        for( $i = 0; $i < 3; $i++ ) {
343
            if( $denorm && $i == 2 && !isset( $point->z ) ) {
344
                continue;
345
            }
346
            if( $i == 0 ) {
347
                $v = $xin;
348
                $t = 'x';
349
            } else if( $i == 1 ) {
350
                $v = $yin;
351
                $t = 'y';
352
            } else {
353
                $v = $zin;
354
                $t = 'z';
355
            }
356
            switch( $crs->axis[$i] ) {
357
                case 'e':
358
                    $point[$t] = $v;
359
                    break;
360
                case 'w':
361
                    $point[$t] = -$v;
362
                    break;
363
                case 'n':
364
                    $point[$t] = $v;
365
                    break;
366
                case 's':
367
                    $point[$t] = -$v;
368
                    break;
369
                case 'u':
370
                    if( isset( $point[$t] ) ) {
371
                        $point->z = $v;
372
                    }
373
                    break;
374
                case 'd':
375
                    if( isset( $point[$t] ) ) {
376
                        $point->z = -$v;
377
                    }
378
                    break;
379
                default :
380
                    throw(new Exception( "ERROR: unknow axis (" . $crs->axis[$i] . ") - check definition of " . $crs->projName ));
381
                    return null;
382
            }
383
        }
384
        return $point;
385
    }
386
 
387
    /**
388
     * Function: reportError
389
     * An internal method to report errors back to user.
390
     * Override this in applications to report error messages or throw exceptions.
391
     */
392
    public static function reportError( $msg ) {
393
        //console.log(msg);
394
        echo $msg . "<br />\n";
395
    }
396
 
397
    /**
398
     * Function : loadScript
399
     * adapted from original. PHP is simplier.
400
     */
401
    public static function loadScript( $filename, $onload = null, $onfail = null, $loadCheck = null ) {
402
 
403
        if( stripos($filename, 'http://') !== false ) {
404
            return @file_get_contents($filename);
405
        }
406
        elseif( file_exists( $filename ) ) {
407
            require_once($filename);
408
            return true;
409
        }
410
        else {
411
            throw(new Exception( "File $filename could not be found or was not able to be loaded." ));
412
            return false;
413
        }
414
    }
415
 
416
    /**
417
     * Function: extend
418
     * Copy all properties of a source object to a destination object.  Modifies
419
     *     the passed in destination object.  Any properties on the source object
420
     *     that are set to undefined will not be (re)set on the destination object.
421
     *
422
     * Parameters:
423
     * destination - {Object} The object that will be modified
424
     * source - {Object} The object with properties to be set on the destination
425
     *
426
     * Returns:
427
     * {Object} The destination object.
428
     */
429
    public static function extend( $destination, $source ) {
430
        if( $source != null )
431
            foreach( $source as $key => $value ) {
432
                $destination->$key = $value;
433
            }
434
        return $destination;
435
    }
436
 
437
}