PageRenderTime 47ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/library/referencing/src/main/java/org/geotools/referencing/operation/builder/AdvancedAffineBuilder.java

https://bitbucket.org/dyp/geotools
Java | 572 lines | 268 code | 111 blank | 193 comment | 21 complexity | 10d7b81c599ef325bfc5d9e9a7d3be76 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause
  1. /*
  2. * GeoTools - The Open Source Java GIS Toolkit
  3. * http://geotools.org
  4. *
  5. * (C) 2007-2008, Open Source Geospatial Foundation (OSGeo)
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation;
  10. * version 2.1 of the License.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. */
  17. package org.geotools.referencing.operation.builder;
  18. import java.util.HashMap;
  19. import java.util.List;
  20. import java.util.Map;
  21. import javax.vecmath.MismatchedSizeException;
  22. import org.opengis.geometry.MismatchedDimensionException;
  23. import org.opengis.geometry.MismatchedReferenceSystemException;
  24. import org.opengis.referencing.FactoryException;
  25. import org.opengis.referencing.operation.MathTransform;
  26. import org.geotools.referencing.operation.matrix.GeneralMatrix;
  27. import org.geotools.referencing.operation.transform.AffineTransform2D;
  28. import org.geotools.referencing.operation.transform.ProjectiveTransform;
  29. /**
  30. * Builder for affine transformation with possibility to set several constrains
  31. * for affine parameters that will be respected during calculation. This is convenient
  32. * for example to use when you want affine transformation with skew parameter equal to zero.
  33. * Development carried out thanks to R&D grant DC08P02OUK006 - Old Maps Online
  34. * (www.oldmapsonline.org) from Ministry of Culture of the Czech Republic
  35. *
  36. * @author jezekjan
  37. * @since
  38. *
  39. *
  40. * @source $URL$
  41. * @version $Id$
  42. */
  43. public class AdvancedAffineBuilder extends MathTransformBuilder {
  44. /**mark for key to specify sx - scale in x constrain */
  45. public static final String SX = "sx";
  46. /**mark for key to specify sy - scale in y constrain */
  47. public static final String SY = "sy";
  48. /**mark for key to specify sxy - skew constrain */
  49. public static final String SXY = "sxy";
  50. /**mark for key to specify phix - rotation constrain */
  51. public static final String PHIX = "phix";
  52. /**mark for key to specify phix - rotation constrain */
  53. public static final String PHIY = "phiy";
  54. /**mark for key to specify tx - translation in x constrain */
  55. public static final String TX = "tx";
  56. /**mark for key to specify ty - translation in y constrain */
  57. public static final String TY = "ty";
  58. /** translation in x */
  59. private double tx;
  60. /** translation in y */
  61. private double ty;
  62. /** scale in x */
  63. private double sx;
  64. /** scale in y */
  65. private double sy;
  66. /** x rotation in radians */
  67. private double phix;
  68. /** x rotation in radians */
  69. private double phiy;
  70. /** max difference for iteration */
  71. private double dif = 0.0000001;
  72. /** max number of steps for iteration */
  73. private int steps = 100;
  74. /** Map of constrains - parameter name as key and its required value*/
  75. private Map<String, Double> valueConstrain = new HashMap<String, Double>();
  76. /** Map of constrains - parameters (represented by string) are equal to each other*/
  77. private Map<String, String> equalConstrain = new HashMap<String, String>();
  78. /**Affine transformation for approximate values*/
  79. private final AffineTransform2D affineTrans;
  80. /**
  81. * Constructs builder from set of GCPs
  82. * @param vectors GCPs
  83. */
  84. public AdvancedAffineBuilder(final List<MappedPosition> vectors)
  85. throws MismatchedSizeException, MismatchedDimensionException,
  86. MismatchedReferenceSystemException, FactoryException {
  87. /**
  88. * use constructor with approximate values taken from 6 parameters of affine transform
  89. */
  90. this(vectors, (AffineTransform2D) (new AffineTransformBuilder(vectors).getMathTransform()));
  91. }
  92. /**
  93. * Constructs affine transform from GCPs and approximate values for calculation. This constructor
  94. * should be used when the default calculation is divergating.
  95. * @param vectors GCPs
  96. * @param affineTrans approximate affine transformation
  97. * @throws MismatchedSizeException
  98. * @throws MismatchedDimensionException
  99. * @throws MismatchedReferenceSystemException
  100. * @throws FactoryException
  101. */
  102. public AdvancedAffineBuilder(final List<MappedPosition> vectors, AffineTransform2D affineTrans)
  103. throws MismatchedSizeException, MismatchedDimensionException,
  104. MismatchedReferenceSystemException, FactoryException {
  105. super.setMappedPositions(vectors);
  106. /**
  107. * sets approximate values
  108. */
  109. this.affineTrans = affineTrans;
  110. AffineToGeometric a2g = new AffineToGeometric(affineTrans);
  111. sx = a2g.getXScale();
  112. sy = a2g.getYScale();
  113. phix = a2g.getXRotation();
  114. phiy = a2g.getYRotation();
  115. tx = a2g.getXTranslate();
  116. ty = a2g.getYTranslate();
  117. }
  118. /**
  119. * Sets constrain that {@code param} is equal to {@code value}. Be aware that the calculation may diverge in the case you set some values
  120. * that are not 'close' to approximate values. I the case of divergence you can set approximate
  121. * values using proper constructor
  122. * @param param parameter name - set one of AdvancedAffineBuilder static variables.
  123. * @param value required value
  124. */
  125. public void setConstrain(String param, double value) {
  126. valueConstrain.put(param, value);
  127. }
  128. /**
  129. * Clears all constrains
  130. */
  131. public void clearConstrains() {
  132. valueConstrain.clear();
  133. }
  134. /**
  135. * Generates A matrix (Matrix of derivation of affine equation). Each column is derivation by
  136. * each transformation parameter (sx, sy, sxy, phi, tx,ty). The rows are derivations of fx and fy.
  137. *
  138. * @return A matrix
  139. */
  140. protected GeneralMatrix getA() {
  141. GeneralMatrix A = new GeneralMatrix(2 * this.getMappedPositions().size(), 6);
  142. double cosphix = Math.cos(phix);
  143. double sinphix = Math.sin(phix);
  144. double cosphiy = Math.cos(phiy);
  145. double sinphiy = Math.sin(phiy);
  146. /**
  147. * Each row is calculated with values of proper GCPs
  148. */
  149. for (int j = 0; j < (A.getNumRow() / 2); j++) {
  150. double x = getSourcePoints()[j].getOrdinate(0);
  151. double y = getSourcePoints()[j].getOrdinate(1);
  152. /*************************
  153. *
  154. * Derivation X
  155. *
  156. **************************/
  157. double dxsx = cosphix*x;
  158. double dxsy = - sinphiy * y;
  159. double dxphix = -sx*sinphix* x;
  160. double dxphiy = -sy*cosphiy* y ;
  161. double dxtx = 1;
  162. double dxty = 0;
  163. /*************************
  164. *
  165. * Derivation Y
  166. *
  167. ***********************/
  168. double dysx = sinphix * x;
  169. double dysy = cosphiy * y;
  170. double dyphix = sx*cosphix*x;
  171. double dyphiy = -sy*sinphiy* y ;
  172. double dytx = 0;
  173. double dyty = 1;
  174. A.setRow(j, new double[] { dxsx, dxsy, dxphix, dxphiy, dxtx, dxty });
  175. A.setRow(A.getNumRow()/2 + j, new double[] { dysx, dysy, dyphix, dyphiy, dytx, dyty });
  176. }
  177. return A;
  178. }
  179. /**
  180. * Fill L matrix. This matrix contains differences between expected value and value
  181. * calculated from affine parameters
  182. * @return l matrix
  183. */
  184. protected GeneralMatrix getL() {
  185. GeneralMatrix l = new GeneralMatrix(2 * this.getMappedPositions().size(), 1);
  186. double cosphix = Math.cos(phix);
  187. double sinphix = Math.sin(phix);
  188. double cosphiy = Math.cos(phiy);
  189. double sinphiy = Math.sin(phiy);
  190. for (int j = 0; j < (l.getNumRow() / 2); j++) {
  191. double x = getSourcePoints()[j].getOrdinate(0);
  192. double y = getSourcePoints()[j].getOrdinate(1);
  193. /* a1 is target value - transfomed value*/
  194. double dx = getTargetPoints()[j].getOrdinate(0)
  195. - (sx*cosphix*x - sy*sinphiy*y + tx);
  196. double dy = getTargetPoints()[j].getOrdinate(1)
  197. - (sx*sinphix*x + sy*cosphiy*y + ty);
  198. l.setElement(j, 0, dx);
  199. l.setElement((l.getNumRow() / 2) + j, 0, dy);
  200. }
  201. return l;
  202. }
  203. /**
  204. * Ask for dx matrix with default number of iterations and precision constrain.
  205. * @return dx matrix
  206. * @throws FactoryException
  207. */
  208. private GeneralMatrix getDxMatrix() throws FactoryException {
  209. return getDxMatrix(dif, steps);
  210. }
  211. /**
  212. * Get matrix of affine coefficients as a result of LSM.
  213. * @param tolerance tolerance for iteration.
  214. * @param maxSteps max steps of iteration
  215. * @return dx matrix
  216. * @throws FactoryException
  217. */
  218. private GeneralMatrix getDxMatrix(double tolerance, int maxSteps)
  219. throws FactoryException {
  220. /**
  221. * Matrix of new calculated coefficients
  222. */
  223. GeneralMatrix xNew = new GeneralMatrix(6, 1);
  224. /**
  225. * Matrix of coefficients calculated in previous iteration
  226. */
  227. GeneralMatrix xOld = new GeneralMatrix(6, 1);
  228. /**
  229. * Difference between each steps of iteration
  230. */
  231. GeneralMatrix dxMatrix = new GeneralMatrix(6, 1);
  232. /**
  233. * Zero matrix
  234. */
  235. GeneralMatrix zero = new GeneralMatrix(6, 1);
  236. zero.setZero();
  237. /**
  238. * Result
  239. */
  240. GeneralMatrix xk = new GeneralMatrix(6 + valueConstrain.size(), 1);
  241. // i is a number of iterations
  242. int i = 0;
  243. // iteration
  244. do {
  245. xOld.set(new double[] { sx, sy, phix, phiy, tx, ty });
  246. GeneralMatrix A = getA();
  247. GeneralMatrix l = getL();
  248. GeneralMatrix AT = A.clone();
  249. AT.transpose();
  250. GeneralMatrix ATA = new GeneralMatrix(6, 6);
  251. GeneralMatrix ATl = new GeneralMatrix(6, 1);
  252. ATA.mul(AT, A);
  253. ATl.mul(AT, l);
  254. /**constrains**/
  255. GeneralMatrix AB = createAB(ATA, getB());
  256. AB.invert();
  257. AB.negate();
  258. GeneralMatrix AU = createAU(ATl, getU());
  259. xk.mul(AB, AU);
  260. xk.copySubMatrix(0, 0, 6, xk.getNumCol(), 0, 0, dxMatrix);
  261. dxMatrix.negate();
  262. // New values of x = dx + previous values
  263. xOld.negate();
  264. xNew.sub(dxMatrix, xOld);
  265. // New values are setup for another iteration
  266. sx = xNew.getElement(0, 0);
  267. sy = xNew.getElement(1, 0);
  268. phix = xNew.getElement(2, 0);
  269. phiy = xNew.getElement(3, 0);
  270. tx = xNew.getElement(4, 0);
  271. ty = xNew.getElement(5, 0);
  272. i++;
  273. if (i > maxSteps) { //&& oldDxMatrix.getElement(0, 0) < dxMatrix.getElement(0, 0)){
  274. throw new FactoryException("Calculation of transformation is divergating - try to set proper aproximate values");
  275. }
  276. } while ((!dxMatrix.equals(zero, tolerance)));
  277. xNew.transpose();
  278. return xNew;
  279. }
  280. @Override
  281. public int getMinimumPointCount() {
  282. return 3;
  283. }
  284. /**
  285. * Fill matrix of derivations of constrains by affine parameters.
  286. * @return B matrix
  287. */
  288. protected GeneralMatrix getB() {
  289. GeneralMatrix B = new GeneralMatrix(valueConstrain.size(), 6);
  290. int i = 0;
  291. if (valueConstrain.containsKey(SX)) {
  292. B.setRow(i, new double[] { 1, 0, 0, 0, 0, 0 });
  293. i++;
  294. }
  295. if (valueConstrain.containsKey(SY)) {
  296. B.setRow(i, new double[] { 0, 1, 0, 0, 0, 0 });
  297. i++;
  298. }
  299. if (valueConstrain.containsKey(PHIX)) {
  300. B.setRow(i, new double[] { 0, 0, 1, 0, 0, 0 });
  301. i++;
  302. }
  303. if (valueConstrain.containsKey(PHIY)) {
  304. B.setRow(i, new double[] { 0, 0, 0, 1, 0, 0 });
  305. i++;
  306. }
  307. if (valueConstrain.containsKey(TX)) {
  308. B.setRow(i, new double[] { 0, 0, 0, 0, 1, 0 });
  309. i++;
  310. }
  311. if (valueConstrain.containsKey(TY)) {
  312. B.setRow(i, new double[] { 0, 0, 0, 0, 0, 1 });
  313. i++;
  314. }
  315. if (valueConstrain.containsKey(SXY)) {
  316. B.setRow(i, new double[] { 0, 0, -1, 1, 0, 0 });
  317. i++;
  318. }
  319. return B;
  320. }
  321. /**
  322. * Fill matrix of constrain values (e.g. for constrain skew = 0 the value is 0)
  323. * @return U matrix
  324. */
  325. protected GeneralMatrix getU() {
  326. GeneralMatrix U = new GeneralMatrix(valueConstrain.size(), 1);
  327. int i = 0;
  328. if (valueConstrain.containsKey(SX)) {
  329. U.setRow(i, new double[] { -sx + valueConstrain.get(SX) });
  330. i++;
  331. }
  332. if (valueConstrain.containsKey(SY)) {
  333. U.setRow(i, new double[] { -sy + valueConstrain.get(SY) });
  334. i++;
  335. }
  336. if (valueConstrain.containsKey(PHIX)) {
  337. U.setRow(i, new double[] { -phix + valueConstrain.get(PHIX)});
  338. i++;
  339. }
  340. if (valueConstrain.containsKey(PHIY)) {
  341. U.setRow(i, new double[] { -phiy + valueConstrain.get(PHIY) });
  342. i++;
  343. }
  344. if (valueConstrain.containsKey(TX)) {
  345. U.setRow(i, new double[] { -tx + valueConstrain.get(TX) });
  346. i++;
  347. }
  348. if (valueConstrain.containsKey(SXY)) {
  349. U.setRow(i, new double[] { (phix-phiy) + valueConstrain.get(SXY) });
  350. i++;
  351. } else if (valueConstrain.containsKey(TY)) {
  352. U.setRow(i, new double[] { -ty + valueConstrain.get(TY) });
  353. i++;
  354. }
  355. return U;
  356. }
  357. /**
  358. * Joins A <sup>T</sup> matrix with L
  359. * @param ATl
  360. * @param U
  361. * @return matrix constructs from ATl and U
  362. */
  363. private GeneralMatrix createAU(GeneralMatrix ATl, GeneralMatrix U) {
  364. GeneralMatrix AU = new GeneralMatrix(ATl.getNumRow() + U.getNumRow(), ATl.getNumCol());
  365. ATl.copySubMatrix(0, 0, ATl.getNumRow(), ATl.getNumCol(), 0, 0, AU);
  366. U.copySubMatrix(0, 0, U.getNumRow(), U.getNumCol(), ATl.getNumRow(), 0, AU);
  367. return AU;
  368. }
  369. /**
  370. * Joins A matrix with B.
  371. * result is:
  372. * (A B )
  373. * (B 0 )
  374. *
  375. * @param ATA
  376. * @param B
  377. * @return matrix constructs from ATA and B
  378. */
  379. private GeneralMatrix createAB(GeneralMatrix ATA, GeneralMatrix B) {
  380. GeneralMatrix BT = B.clone();
  381. BT.transpose();
  382. GeneralMatrix AAB = new GeneralMatrix(ATA.getNumRow() + B.getNumRow(),
  383. ATA.getNumCol() + BT.getNumCol());
  384. ATA.copySubMatrix(0, 0, ATA.getNumRow(), ATA.getNumCol(), 0, 0, AAB);
  385. B.copySubMatrix(0, 0, B.getNumRow(), B.getNumCol(), ATA.getNumRow(), 0, AAB);
  386. BT.copySubMatrix(0, 0, BT.getNumRow(), BT.getNumCol(), 0, ATA.getNumCol(), AAB);
  387. GeneralMatrix zero = new GeneralMatrix(B.getNumRow(), B.getNumRow());
  388. zero.setZero();
  389. zero.copySubMatrix(0, 0, zero.getNumRow(), zero.getNumCol(), B.getNumCol(), B.getNumCol(),
  390. AAB);
  391. return AAB;
  392. }
  393. /**
  394. * Calculates coefficients of Projective transformation matrix from geometric parameters.
  395. *
  396. * @return Projective Matrix
  397. * @throws FactoryException
  398. */
  399. protected GeneralMatrix getProjectiveMatrix() throws FactoryException {
  400. GeneralMatrix M = new GeneralMatrix(3, 3);
  401. /**
  402. * Runs calculation of parameter values
  403. */
  404. double[] param = getDxMatrix().getElements()[0];
  405. /**
  406. * calcuates matrix coefficients form geometric coefficients
  407. */
  408. double a11 = sx * Math.cos(phix);
  409. double a12 = -sy * Math.sin(phiy);
  410. double a21 = sx* Math.sin(phix);
  411. double a22 = sy * Math.cos(phiy);
  412. /**
  413. * Fill the metrix
  414. */
  415. double[] m0 = { a11, a12, param[4] };
  416. double[] m1 = { a21, a22, param[5] };
  417. double[] m2 = { 0, 0, 1 };
  418. M.setRow(0, m0);
  419. M.setRow(1, m1);
  420. M.setRow(2, m2);
  421. return M;
  422. }
  423. @Override
  424. protected MathTransform computeMathTransform() throws FactoryException {
  425. if (valueConstrain.size() == 0) {
  426. return affineTrans;
  427. }
  428. return ProjectiveTransform.create(getProjectiveMatrix());
  429. }
  430. /**
  431. * Returns difference that is required between steps in iteration
  432. * @return max difference that is required for iteration steps
  433. */
  434. public double getMaxIterationDifference() {
  435. return dif;
  436. }
  437. /**
  438. * Sets difference that is required between steps in iteration.
  439. * @param dif
  440. */
  441. public void setMaxIterationDifference(double dif) {
  442. this.dif = dif;
  443. }
  444. /**
  445. * Return max number of iteration steps. If the difference between calculated values
  446. * in each iteration steps is still bigger than required then Exception is thrown.
  447. * This is not the number that was really needed for iteration.
  448. * @return max number of iteration steps.
  449. */
  450. public int getNumberOfIterationSteps() {
  451. return steps;
  452. }
  453. /**
  454. * Sets max number of iteration steps. If the difference between calculated values
  455. * in each iteration steps is still bigger than required than Exception is thrown.
  456. * @param steps max number of iterations.
  457. */
  458. public void setNumberOfIterationSteps(int steps) {
  459. this.steps = steps;
  460. }
  461. }