Statistics
| Revision:

root / trunk / extensions / extGeoreferencing / src / org / gvsig / georeferencing / GeoOperations.java @ 12546

History | View | Annotate | Download (16.7 KB)

1
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
2
 *
3
 * Copyright (C) 2006 IVER T.I. and Generalitat Valenciana.
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
18
 */
19
package org.gvsig.georeferencing;
20

    
21
import java.awt.geom.AffineTransform;
22
import java.awt.geom.Point2D;
23
import java.io.BufferedOutputStream;
24
import java.io.BufferedReader;
25
import java.io.BufferedWriter;
26
import java.io.DataOutputStream;
27
import java.io.File;
28
import java.io.FileInputStream;
29
import java.io.FileNotFoundException;
30
import java.io.FileOutputStream;
31
import java.io.IOException;
32
import java.io.InputStreamReader;
33
import java.io.OutputStreamWriter;
34
import java.util.zip.DataFormatException;
35

    
36
import org.cresques.cts.ICoordTrans;
37
import org.gvsig.georeferencing.utils.AffineT;
38
import org.gvsig.georeferencing.utils.GeoUtils;
39
import org.gvsig.georeferencing.utils.PointT;
40
import org.gvsig.raster.dataset.BandList;
41
import org.gvsig.raster.dataset.FileNotOpenException;
42
import org.gvsig.raster.dataset.GeoInfo;
43
import org.gvsig.raster.dataset.IBuffer;
44
import org.gvsig.raster.dataset.InvalidSetViewException;
45
import org.gvsig.raster.dataset.RasterDataset;
46
import org.gvsig.raster.dataset.RasterDriverException;
47
import org.gvsig.raster.dataset.RasterMetaFileTags;
48
import org.gvsig.raster.dataset.io.rmf.RmfBlocksManager;
49
import org.gvsig.raster.dataset.serializer.GeoInfoRmfSerializer;
50
import org.gvsig.raster.datastruct.Extent;
51
import org.xmlpull.v1.XmlSerializer;
52

    
53
import com.iver.cit.gvsig.fmap.layers.FLyrPoints;
54

    
55

    
56
/** 
57
 * Operaciones necesarias para la georreferenciaci?n a partir de la capa de puntos. 
58
 * En base a unos puntos de control de origen y otros de destino se crea una matriz
59
 * de transformaci?n para asignar la nueva posici?n a la imagen y crear ficheros 
60
 * de georreferenciaci?n en dos formatos: worldfile y rasterMetaFile
61
 * 
62
 * @author Nacho Brodin (brodin_ign@gva.es)
63
 */
64
public class GeoOperations{
65
        
66
        //**********************Vars**********************************
67
        private int                         order = 1;
68
        private PointT[]                 src = null;
69
        private PointT[]                 dst = null;
70
        private AffineT                affine = null;
71
        private boolean         createWorldFile = false;
72
        //**********************End Vars******************************
73
        
74
        //**********************Methods*******************************
75
        /**
76
         * Constructor
77
         */
78
        public GeoOperations(){}
79
        
80
        /**
81
         * Constructor. Crea las estructuras de puntos y calcula la transformaci?n af?n.
82
         * @param lyr
83
         */
84
        public GeoOperations(FLyrPoints lyr){
85
                FLyrPoints         lyrPoints = lyr;
86
                src = new PointT[lyr.getCountActivePoints()]; 
87
                dst = new PointT[lyr.getCountActivePoints()];
88
                int nPoint = 0;
89
                for(int i = 0; i<lyr.getCountPoints(); i++){
90
                        if(lyr.getPoint(i).active == true){
91
                                src[nPoint] = new PointT();
92
                                dst[nPoint] = new PointT();
93
                                src[nPoint].setX(lyr.getPoint(i).pixelPoint.getX());
94
                                src[nPoint].setY(lyr.getPoint(i).pixelPoint.getY());
95
                                src[nPoint].setI(lyr.getCountPoints());
96
                                dst[nPoint].setX(lyr.getPoint(i).mapPoint.getX());
97
                                dst[nPoint].setY(lyr.getPoint(i).mapPoint.getY());
98
                                dst[nPoint].setI(lyr.getCountPoints());
99
                                nPoint++;
100
                        }
101
                }
102
                
103
                if(lyr.getCountActivePoints() >= 3 * 10)
104
                        order = 3;
105
                else if(lyr.getCountActivePoints() >= 3 * 6)
106
                        order = 2;
107
                else
108
                        order = 1;
109

    
110
                affine = new AffineT();
111
                
112
                //Calcular la transformaci?n afin por m?nimos cuadrados
113
                affine = computeLeastSquaresAffine(affine, src, dst, order);
114
        }
115
        
116
        /**
117
         * A partir de la transformaci?n af?n creada en el construtor
118
         * genera un fichero de georreferenciaci?n para la imagen.
119
         * @param lyr                Capa de puntos
120
         * @param order        Orden del sistema de ecuaciones
121
         * @param widthPx        Ancho en pixeles de la imagen a georreferenciar
122
         * @param heightPx        Alto en pixeles de la imagen a georreferenciar
123
         * @param file                Nombre del fichero raster a georreferenciar
124
         */
125
        public void createGeorefFile(int widthPx, int heightPx, String file){
126
                try{
127
                        File f = new File(file);
128
                        String nameWorldFile = f.getPath().substring(0, f.getPath().lastIndexOf(".")) + GeoUtils.getWorldFileExtensionFromFileName(file);
129
                        if(createWorldFile)
130
                                createWorldFile(affine, widthPx, heightPx, nameWorldFile);
131
                        createRasterMetaFile(affine, widthPx, heightPx, nameWorldFile.substring(0, nameWorldFile.lastIndexOf("."))+".rmf");
132
                }catch(IOException ex){
133
                        System.err.println("Can't create WorldFile");
134
                }
135
        }
136
        
137
        /**
138
         * A partir de la transformaci?n af?n calculada en el contructor
139
         * transforma los puntos pasados como par?metros.
140
         * @param lyr                Capa de puntos
141
         * @param list                Lista de puntos a transformar
142
         * @return                  Lista de puntos transformados
143
         */
144
        public Point2D[] transformPoints(Point2D[] list){
145
                Point2D[] result = new Point2D[list.length];
146
                for(int i = 0; i < list.length;i++){
147
                        double[] pto = transformPoint((int)list[i].getX(), (int)list[i].getY(), affine);
148
                        result[i] = new Point2D.Double(pto[0], pto[1]);
149
                }
150
                return result;
151
        }
152
        
153
        /**
154
         * Crea un fichero de georreferenciaci?n (worldfile) a partir de la transformaci?n af?n. Para
155
         * esto necesita obtener las coordenadas reales de la coordenada en pixels (0,0) y calcular
156
         * el tama?o de pixel. 
157
         * @param affine                Transformaci?n
158
         * @param widthPx                Ancho en pixeles de la imagen a georreferenciar
159
         * @param heightPx                Alto en pixeles de la imagen a georreferenciar
160
         * @param nameWordlFile        Nombre del fichero de georreferenciaci?n
161
         * @throws IOException
162
         */
163
        private void createWorldFile(AffineT affine, int widthPx, int heightPx, String nameWordlFile) throws IOException {
164
                StringBuffer data = new StringBuffer();
165
                //double[] begin = transformPoint(0, 0, affine);
166
                //double[] end = transformPoint(widthPx, heightPx, affine);
167
                //double pixelSizeX = (end[0] - begin[0])/(widthPx - 1);
168
                //double pixelSizeY = (end[1] - begin[1])/(heightPx - 1);
169

    
170
                File f = new File(nameWordlFile);
171
                DataOutputStream dos = new DataOutputStream( new BufferedOutputStream(new FileOutputStream(f)) );
172
                
173
            data.append(affine.getCofX(1)+"\n");//pixelSizeX+"\n");
174
            data.append(affine.getCofX(2)+"\n");
175
            data.append(affine.getCofY(1)+"\n");
176
            data.append(affine.getCofY(2)+"\n");//(-1  * pixelSizeY)+"\n");
177
            data.append(affine.getCofX(0)+"\n");//""+begin[0]+"\n");
178
            data.append(affine.getCofY(0)+"\n");//""+end[1]+"\n");
179
            
180
            dos.writeBytes(data.toString());
181
                dos.close();
182
        }
183
                
184
        /**
185
         * Si no existe crea un fichero .rmf con la georreferenciaci?n de la imagen. Si existe este 
186
         * deber? a?adir o modificar la informaci?n de georreferenciaci?n en el fichero.
187
         * Para esto necesita obtener las coordenadas reales de la coordenada en pixels (0,0) y 
188
         * calcular el tama?o de pixel. 
189
         * @param affine                Transformaci?n
190
         * @param widthPx                Ancho en pixeles de la imagen a georreferenciar
191
         * @param heightPx                Alto en pixeles de la imagen a georreferenciar
192
         * @param nameWordlFile        Nombre del fichero de georreferenciaci?n
193
         * @throws IOException
194
         */
195
        private void createRasterMetaFile(AffineT affine, int widthPx, int heightPx, String nameWorldFile) throws IOException {
196
                double[] min = transformPoint(0, 0, affine);
197
                double[] max = transformPoint(widthPx, heightPx, affine);
198
                
199
                RmfBlocksManager manager = new RmfBlocksManager(nameWorldFile);
200
                DatasetSer dataset = new DatasetSer();
201
                dataset.setExtent(new Extent(min[0], max[1], max[0], min[1]));
202
                AffineTransform at = new AffineTransform(affine.getCofX(1), affine.getCofX(2), affine.getCofY(1), affine.getCofY(2), affine.getCofX(0), affine.getCofY(0));
203
                dataset.setAffineTransform(at);
204
                dataset.w = widthPx;
205
                dataset.h = heightPx;
206
                GeoInfoRmfSerializer ser = new GeoInfoRmfSerializer(dataset);
207
                manager.addClient(ser);
208
                
209
                try {
210
                        manager.write();
211
                } catch (FileNotFoundException e) {
212
                        e.printStackTrace();
213
                } catch (IOException e) {
214
                        e.printStackTrace();
215
                }
216
        }
217
                
218
        /**
219
         * Calcula la transformaci?n af?n a partir de los puntos introducidos por el m?todo de 
220
         * m?nimos cuadrados.
221
         * @param af        Transformaci?n af?n
222
         * @param src        Puntos de entrada en coordenadas pixel
223
         * @param dst        Puntos de destino en coordenadas del mundo         
224
         * @param order        Orden del sistema de ecuaciones
225
         */
226
        public AffineT computeLeastSquaresAffine(AffineT af, PointT[] src, PointT[] dst, int order){
227
                double[] b = new double[dst.length];
228
                int i,cofs;
229

    
230
                af.setOrder(order);
231
                cofs = order <= 2 ? order*3 : 10;
232
                af.mallocCofs(cofs);
233
                
234
                if(src.length == 2){
235
                        af.setCofX(0, 2);
236
                        af.setCofY(0, 1);
237
                        double distWCX = dst[1].getX() - dst[0].getX();
238
                        double distWCY = dst[1].getY() - dst[0].getY();
239
                        double distPxX = src[1].getX() - src[0].getX();
240
                        double distPxY = src[1].getY() - src[0].getY();
241
                        double psX = distWCX / distPxX;
242
                        double psY = distWCY / distPxY;
243
                        af.setCofX(psX, 1);
244
                        af.setCofY(psY, 2);
245
                        
246
                        double initWCX = dst[0].getX() - ((src[0].getX() + 1) * psX);
247
                        double initWCY = dst[0].getY() - ((src[0].getY() + 1) * psY);
248
                        
249
                        af.setCofX(initWCX, 0);
250
                        af.setCofY(initWCY, 0);
251
                        return af;
252
                }
253
                
254
                //First compute the X cofs  
255
                for(i = 0; i < dst.length; i++) {    
256
                        b[i] = dst[i].getX();  
257
                }
258
                
259
                af.setCofX(singleLeastSquaresAffine(af.getXcofs(), src, b, order));
260
                
261
                //Now compute the Y cofs  
262
                for(i = 0; i < dst.length; i++) {    
263
                        b[i] = dst[i].getY();  
264
                }
265
                
266
                af.setCofY(singleLeastSquaresAffine(af.getYcofs(), src, b, order));
267
                
268
                return af;
269
        }
270
        
271
        private double[] singleLeastSquaresAffine(double[] a, PointT[] src, double[] dst, int order){
272
                int i,j;
273
                int n = dst.length;
274

    
275
                int points = order <= 2 ? order * 3 : 10;        
276
                double[][] combined = new double[points][points + 1];
277
                double[][] A = new double[n + 1][points];
278
                double[][] b = new double[n + 1][1];
279
                
280
                for(i = 0; i < n; i++) {
281
                        A[i][0] = 1.0;
282
                        A[i][1] = src[i].getX();
283
                        A[i][2] = src[i].getY();
284
                        if(order > 1) {
285
                                A[i][3] = src[i].getX() * src[i].getX();
286
                                A[i][4] = src[i].getX() * src[i].getY();
287
                                A[i][5] = src[i].getY() * src[i].getY();
288
                        }
289
                        if(order > 2) {
290
                                A[i][6] = src[i].getX() * src[i].getX() * src[i].getX();
291
                                A[i][7] = src[i].getX() * src[i].getX() * src[i].getY();
292
                                A[i][8] = src[i].getX() * src[i].getY() * src[i].getY();
293
                                A[i][9] = src[i].getY() * src[i].getY() * src[i].getY();
294
                        }
295
                        b[i][0] = dst[i];
296
                }
297
                
298
                double[][] Atrans = GeoUtils.transpose(A, n, points);
299
                double[][] left = GeoUtils.multmatrix(Atrans,A,points,n,points);
300
                double[][] right = GeoUtils.multmatrix(Atrans,b,points,n,1);
301
                
302
                for(i = 0; i < points; i++) {
303
                        combined[i][0] = right[i][0];
304
                        for(j = 0; j < points; j++) {
305
                                combined[i][j+1] = left[i][j];
306
                        }
307
                }
308
                
309
                try{
310
                        combined = solve(combined, points, points+1);
311
                }catch(DataFormatException ex){
312
                        System.err.println("Can't solve matrix");
313
                }
314

    
315
                for(i = 0; i < points; i++) {
316
                        a[i] = combined[i][0];
317
                }
318
                return a;
319
        }
320
        
321
        
322
        private double[][] solve(double[][] mat,int rows,int cols)throws DataFormatException{
323
                int i,j,k;
324
                double d,tmp;
325
                int big;
326

    
327
                for(i = 0; i < rows; i++) {
328
                        // Find largest row
329
                                big = i;
330
                                for(j = i; j < rows; j++) {
331
                                        if(Math.abs(mat[j][i+1]) > Math.abs(mat[big][i+1])) {
332
                                                big = j;
333
                                        }
334
                                }
335
                                // swap row i and row big
336
                                for(k = 0; k < cols ; k++) {
337
                                        tmp = mat[i][k];
338
                                        mat[i][k] = mat[big][k];
339
                                        mat[big][k] = tmp;
340
                                }
341
                        if(mat[i][i+1] == 0) 
342
                                throw new DataFormatException();
343
                        
344
                        d = 1.0 / mat[i][i+1]; 
345
                        for(j = 0; j < cols ; j++) {
346
                                mat[i][j] *= d;
347
                                //assert(!isnan(mat[i][j]));
348
                        }
349
                        for(k = 0; k < rows; k++) {
350
                                if(k == i)
351
                                        continue;
352
                                if(mat[k][i+1] != 0.0) {
353
                                d = mat[k][i+1] / mat[i][i+1]; 
354
                                        for(j = 0; j < cols ; j++) {
355
                                                mat[k][j] -= d*mat[i][j];
356
                                                //assert(!isnan(mat[k][j]));
357
                                        }
358
                                }
359
                        }
360
                }
361
                return mat;
362
        }
363
        
364
        /**
365
         * Obtiene las coordenadas de un punto en coordenadas del mundo a partir de una transformaci?n 
366
         * y el punto de entrada en coordenadas de la imagen.
367
         * @param sx        Coordenada X del punto de entrada 
368
         * @param syz        Coordenada Y del punto de entrada
369
         * @param af        Transformaci?n
370
         * @return                Punto transformado
371
         */
372
        public double[] transformPoint(int sx, int sy, AffineT af){
373
                double[] p = new double[2];
374
                if(af.getOrder() == 1) {
375
                        p[0] = af.getCofX(0) + af.getCofX(1) * sx + af.getCofX(2) * sy;
376
                        p[1] = af.getCofY(0) + af.getCofY(1) * sx + af.getCofY(2) * sy;
377
                }
378
                else if(af.getOrder() == 2) {
379
                        p = quadTransformPoint(sx, sy, af);
380
                }
381
                else {
382
                        p = cubicTransformPoint(sx, sy, af);
383
                 }
384
                return p;
385
        }
386
        
387
        private static double[] quadTransformPoint(int sx, int sy, AffineT af){
388
                double[] p = new double[2];
389
                p[0] = af.getCofX(0) + af.getCofX(1) * sx + af.getCofX(2) * sy + 
390
                        af.getCofX(3) * sx * sx + af.getCofX(4) * sx * sy + af.getCofX(5) * sy * sy;
391
                p[1] = af.getCofY(0) + af.getCofY(1) * sx + af.getCofY(2) * sy + 
392
                        af.getCofY(3) * sx * sx + af.getCofY(4) * sx * sy + af.getCofY(5) * sy * sy;
393
                return p;
394
        }
395
        
396
        private static double[] cubicTransformPoint(int sx, int sy, AffineT af){
397
                double[] p = new double[2];
398
                p[0] = af.getCofX(0) + af.getCofX(1) * sx + af.getCofX(2) * sy + 
399
                        af.getCofX(3) * sx * sx + af.getCofX(4) * sx * sy + af.getCofX(5) * sy * sy +
400
                        af.getCofX(6) * sx * sx * sx + af.getCofX(7) * sx * sx * sy +
401
                        af.getCofX(8)*sx*sy*sy + af.getCofX(9)*sy*sy*sy;
402
                p[1] = af.getCofY(0) + af.getCofY(1) * sx + af.getCofY(2) * sy + 
403
                        af.getCofY(3) * sx * sx + af.getCofY(4) * sx * sy + af.getCofY(5) * sy * sy +
404
                        af.getCofY(6) * sx * sx * sx + af.getCofY(7) * sx * sx * sy +
405
                        af.getCofY(8) * sx * sy * sy + af.getCofY(9) * sy * sy * sy;
406
                return p;
407
        }        
408
        //**********************End Methods***************************
409
        
410
        //**********************Setters & Getters*********************
411
        /**
412
         * M?todo para notificar si se desea crear o no el fichero de coordenadas .tfw, .wld .jpgw ,... 
413
         * @param createWorldFile
414
         */
415
        public void setCreateWorldFile(boolean createWorldFile) {
416
                this.createWorldFile = createWorldFile;
417
        }
418
        
419
        /**
420
         * Obtiene la extensi?n del fichero de georreferenciaci?n
421
         * @return String con la extensi?n del fichero de georreferenciaci?n dependiendo
422
         * del valor del formato obtenido del servidor. Por defecto asignaremos un .wld 
423
         */
424
        /*private String getExtensionWorldFile(String file){
425
                String ext = file.substring(file.lastIndexOf(".") + 1).toLowerCase();
426
                String extWorldFile = ".wld";
427
            if(ext.equals("tif") || ext.equals("tiff"))
428
                    extWorldFile = ".tfw";
429
            if(ext.equals("jpeg") || ext.equals("jpg"))
430
                    extWorldFile = ".jgw";
431
            if(ext.equals("gif"))
432
                    extWorldFile = ".gfw";
433
            if(ext.equals("png"))
434
                    extWorldFile = ".pgw";
435
            return extWorldFile;
436
        }*/
437
        
438
        /**
439
         * Obtiene la matriz de transformaci?n
440
         * @return AffineT
441
         */
442
        public AffineT getAffine() {
443
                return affine;
444
        }
445
        //**********************End Setters & Getters*****************
446
        
447
        public class DatasetSer extends RasterDataset {
448
                public int w, h;
449
                AffineTransform at = new AffineTransform();
450
                
451
                public void close() {}
452

    
453
                public int getBlockSize() {return 0;}
454

    
455
                public Object getData(int arg0, int arg1, int arg2) throws InvalidSetViewException, FileNotOpenException, RasterDriverException {return null;}
456

    
457
                public int getHeight() {return h;}
458

    
459
                public Extent getView() {return null;}
460

    
461
                public int getWidth() {return w;}
462

    
463
                public IBuffer getWindowRaster(int arg0, int arg1, int arg2, int arg3, BandList arg4, IBuffer arg5) {return null;}
464

    
465
                public IBuffer getWindowRaster(double arg0, double arg1, double arg2, double arg3, BandList arg4, IBuffer arg5, boolean arg6) {return null;}
466

    
467
                public IBuffer getWindowRaster(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, BandList arg6, IBuffer arg7) {return null;}
468

    
469
                public IBuffer getWindowRaster(double arg0, double arg1, double arg2, double arg3, int arg4, int arg5, BandList arg6, IBuffer arg7, boolean arg8) {return null;}
470

    
471
                public GeoInfo load() {return null;}
472

    
473
                public Point2D rasterToWorld(Point2D arg0) {return null;}
474

    
475
                public void reProject(ICoordTrans arg0) {}
476

    
477
                public Object readBlock(int arg0, int arg1) throws InvalidSetViewException, FileNotOpenException, RasterDriverException {return null;}
478

    
479
                public Object readCompleteLine(int arg0, int arg1) throws InvalidSetViewException, FileNotOpenException, RasterDriverException {return null;}
480

    
481
                public void setExtentTransform(double arg0, double arg1, double arg2, double arg3) {}
482

    
483
                public void setView(Extent arg0) {}
484

    
485
                public Point2D worldToRaster(Point2D arg0) { return null;}
486
                
487
                public void setAffineTransform(AffineTransform at) {this.at = at;}
488
                
489
                public AffineTransform getAffineTransform() { return at;}
490
        }
491
}