Class IntersectionMatrix

  1  
  2  
  3 /*
  4  * Copyright (c) 2016 Vivid Solutions.
  5  *
  6  * All rights reserved. This program and the accompanying materials
  7  * are made available under the terms of the Eclipse Public License 2.0
  8  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
  9  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
 10  * and the Eclipse Distribution License is available at
 11  *
 12  * http://www.eclipse.org/org/documents/edl-v10.php.
 13  */
 14 package org.locationtech.jts.geom;
 15  
 16 /**
 17  * Models a <b>Dimensionally Extended Nine-Intersection Model (DE-9IM)</b> matrix. 
 18  * DE-9IM matrix values (such as "212FF1FF2")
 19  * specify the topological relationship between two {@link Geometry}s. 
 20  * This class can also represent matrix patterns (such as "T*T******")
 21  * which are used for matching instances of DE-9IM matrices.
 22  * <p>
 23  * DE-9IM matrices are 3x3 matrices with integer entries.
 24  * The matrix indices {0,1,2} represent the topological locations 
 25  * that occur in a geometry (Interior, Boundary, Exterior).  
 26  * These are provided by the constants 
 27  * {@link Location#INTERIOR}, {@link Location#BOUNDARY}, and {@link Location#EXTERIOR}.
 28  * <p>
 29  * When used to specify the topological relationship between two geometries, 
 30  * the matrix entries represent the possible dimensions of each intersection:
 31  * {@link Dimension#A} = 2, {@link Dimension#L} = 1, {@link Dimension#P} = 0 and {@link Dimension#FALSE} = -1.
 32  * When used to represent a matrix pattern entries can have the additional values 
 33  * {@link Dimension#TRUE} {"T") and {@link Dimension#DONTCARE} ("*"). 
 34  * <p>
 35  * For a description of the DE-9IM and the spatial predicates derived from it, 
 36  * see the following references:
 37  * <ul>
 38  * <li><i><a href="http://www.opengis.org/techno/specs.htm">
 39  * OGC 99-049 OpenGIS Simple Features Specification for SQL</a></i>
 40  * , Section 2.1.13</li>
 41  * <li><i><a href="http://portal.opengeospatial.org/files/?artifact_id=25355">
 42  * OGC 06-103r4 OpenGIS Implementation Standard for Geographic information - Simple feature access - Part 1: Common architecture</a></i>
 43  * , Section 6.1.15 (which provides some further details on certain predicate specifications).
 44  * </li>
 45  * <li>Wikipedia article on <a href="https://en.wikipedia.org/wiki/DE-9IM">DE-9IM</a></li>
 46  * </ul>
 47  * <p>
 48  * Methods are provided to:
 49  *  <UL>
 50  *    <LI>set and query the elements of the matrix in a convenient fashion
 51  *    <LI>convert to and from the standard string representation (specified in
 52  *    SFS Section 2.1.13.2).
 53  *    <LI>test if a matrix matches a given pattern string.
 54  *    <li>test if a matrix (possibly with geometry dimensions) matches a standard named spatial predicate
 55  *  </UL>
 56  *
 57  *@version 1.7
 58  */
 59 public class IntersectionMatrix implements Cloneable {
 60   /**
 61    *  Internal representation of this <code>IntersectionMatrix</code>.
 62    */
 63   private int[][] matrix;
 64  
 65   /**
 66    *  Creates an <code>IntersectionMatrix</code> with <code>FALSE</code>
 67    *  dimension values.
 68    */
 69   public IntersectionMatrix() {
 70     matrix = new int[3][3];
 71     setAll(Dimension.FALSE);
 72   }
 73  
 74   /**
 75    *  Creates an <code>IntersectionMatrix</code> with the given dimension
 76    *  symbols.
 77    *
 78    *@param  elements  a String of nine dimension symbols in row major order
 79    */
 80   public IntersectionMatrix(String elements) {
 81     this();
 82     set(elements);
 83   }
 84  
 85   /**
 86    *  Creates an <code>IntersectionMatrix</code> with the same elements as
 87    *  <code>other</code>.
 88    *
 89    *@param  other  an <code>IntersectionMatrix</code> to copy
 90    */
 91   public IntersectionMatrix(IntersectionMatrix other) {
 92     this();
 93     matrix[Location.INTERIOR][Location.INTERIOR] = other.matrix[Location.INTERIOR][Location.INTERIOR];
 94     matrix[Location.INTERIOR][Location.BOUNDARY] = other.matrix[Location.INTERIOR][Location.BOUNDARY];
 95     matrix[Location.INTERIOR][Location.EXTERIOR] = other.matrix[Location.INTERIOR][Location.EXTERIOR];
 96     matrix[Location.BOUNDARY][Location.INTERIOR] = other.matrix[Location.BOUNDARY][Location.INTERIOR];
 97     matrix[Location.BOUNDARY][Location.BOUNDARY] = other.matrix[Location.BOUNDARY][Location.BOUNDARY];
 98     matrix[Location.BOUNDARY][Location.EXTERIOR] = other.matrix[Location.BOUNDARY][Location.EXTERIOR];
 99     matrix[Location.EXTERIOR][Location.INTERIOR] = other.matrix[Location.EXTERIOR][Location.INTERIOR];
100     matrix[Location.EXTERIOR][Location.BOUNDARY] = other.matrix[Location.EXTERIOR][Location.BOUNDARY];
101     matrix[Location.EXTERIOR][Location.EXTERIOR] = other.matrix[Location.EXTERIOR][Location.EXTERIOR];
102   }
103  
104   /**
105    * Adds one matrix to another.
106    * Addition is defined by taking the maximum dimension value of each position
107    * in the summand matrices.
108    *
109    * @param im the matrix to add
110    */
111   public void add(IntersectionMatrix im)
112   {
113     for (int i = 0; i < 3; i++) {
114       for (int j = 0; j < 3; j++) {
115         setAtLeast(i, j, im.get(i, j));
116       }
117     }
118   }
119  
120   /**
121    *  Tests if the dimension value matches <tt>TRUE</tt>
122    *  (i.e.  has value 0, 1, 2 or TRUE).
123    *
124    *@param  actualDimensionValue     a number that can be stored in the <code>IntersectionMatrix</code>
125    *      . Possible values are <code>{TRUE, FALSE, DONTCARE, 0, 1, 2}</code>.
126    *@return true if the dimension value matches TRUE
127    */
128   public static boolean isTrue(int actualDimensionValue) {
129     if (actualDimensionValue >= 0 || actualDimensionValue  == Dimension.TRUE) {
130       return true;
131     }
132     return false;
133   }
134   
135   /**
136    *  Tests if the dimension value satisfies the dimension symbol.
137    *
138    *@param  actualDimensionValue     a number that can be stored in the <code>IntersectionMatrix</code>
139    *      . Possible values are <code>{TRUE, FALSE, DONTCARE, 0, 1, 2}</code>.
140    *@param  requiredDimensionSymbol  a character used in the string
141    *      representation of an <code>IntersectionMatrix</code>. Possible values
142    *      are <code>{T, F, * , 0, 1, 2}</code>.
143    *@return                          true if the dimension symbol matches
144    *      the dimension value
145    */
146   public static boolean matches(int actualDimensionValue, char requiredDimensionSymbol) {
147     if (requiredDimensionSymbol == Dimension.SYM_DONTCARE) {
148       return true;
149     }
150     if (requiredDimensionSymbol == Dimension.SYM_TRUE && (actualDimensionValue >= 0 || actualDimensionValue
151          == Dimension.TRUE)) {
152       return true;
153     }
154     if (requiredDimensionSymbol == Dimension.SYM_FALSE && actualDimensionValue == Dimension.FALSE) {
155       return true;
156     }
157     if (requiredDimensionSymbol == Dimension.SYM_P && actualDimensionValue == Dimension.P) {
158       return true;
159     }
160     if (requiredDimensionSymbol == Dimension.SYM_L && actualDimensionValue == Dimension.L) {
161       return true;
162     }
163     if (requiredDimensionSymbol == Dimension.SYM_A && actualDimensionValue == Dimension.A) {
164       return true;
165     }
166     return false;
167   }
168  
169   /**
170    *  Tests if each of the actual dimension symbols in a matrix string satisfies the
171    *  corresponding required dimension symbol in a pattern string.
172    *
173    *@param  actualDimensionSymbols    nine dimension symbols to validate.
174    *      Possible values are <code>{T, F, * , 0, 1, 2}</code>.
175    *@param  requiredDimensionSymbols  nine dimension symbols to validate
176    *      against. Possible values are <code>{T, F, * , 0, 1, 2}</code>.
177    *@return                           true if each of the required dimension
178    *      symbols encompass the corresponding actual dimension symbol
179    */
180   public static boolean matches(String actualDimensionSymbols, String requiredDimensionSymbols) {
181     IntersectionMatrix m = new IntersectionMatrix(actualDimensionSymbols);
182     return m.matches(requiredDimensionSymbols);
183   }
184  
185   /**
186    *  Changes the value of one of this <code>IntersectionMatrix</code>s
187    *  elements.
188    *
189    *@param  row             the row of this <code>IntersectionMatrix</code>,
190    *      indicating the interior, boundary or exterior of the first <code>Geometry</code>
191    *@param  column          the column of this <code>IntersectionMatrix</code>,
192    *      indicating the interior, boundary or exterior of the second <code>Geometry</code>
193    *@param  dimensionValue  the new value of the element
194    */
195   public void set(int row, int column, int dimensionValue) {
196     matrix[row][column] = dimensionValue;
197   }
198  
199   /**
200    *  Changes the elements of this <code>IntersectionMatrix</code> to the
201    *  dimension symbols in <code>dimensionSymbols</code>.
202    *
203    *@param  dimensionSymbols  nine dimension symbols to which to set this <code>IntersectionMatrix</code>
204    *      s elements. Possible values are <code>{T, F, * , 0, 1, 2}</code>
205    */
206   public void set(String dimensionSymbols) {
207     for (int i = 0; i < dimensionSymbols.length(); i++) {
208       int row = i / 3;
209       int col = i % 3;
210       matrix[row][col] = Dimension.toDimensionValue(dimensionSymbols.charAt(i));
211     }
212   }
213  
214   /**
215    *  Changes the specified element to <code>minimumDimensionValue</code> if the
216    *  element is less.
217    *
218    *@param  row                    the row of this <code>IntersectionMatrix</code>
219    *      , indicating the interior, boundary or exterior of the first <code>Geometry</code>
220    *@param  column                 the column of this <code>IntersectionMatrix</code>
221    *      , indicating the interior, boundary or exterior of the second <code>Geometry</code>
222    *@param  minimumDimensionValue  the dimension value with which to compare the
223    *      element. The order of dimension values from least to greatest is
224    *      <code>{DONTCARE, TRUE, FALSE, 0, 1, 2}</code>.
225    */
226   public void setAtLeast(int row, int column, int minimumDimensionValue) {
227     if (matrix[row][column] < minimumDimensionValue) {
228       matrix[row][column] = minimumDimensionValue;
229     }
230   }
231  
232   /**
233    *  If row >= 0 and column >= 0, changes the specified element to <code>minimumDimensionValue</code>
234    *  if the element is less. Does nothing if row <0 or column < 0.
235    *
236    *@param  row                    the row of this <code>IntersectionMatrix</code>
237    *      , indicating the interior, boundary or exterior of the first <code>Geometry</code>
238    *@param  column                 the column of this <code>IntersectionMatrix</code>
239    *      , indicating the interior, boundary or exterior of the second <code>Geometry</code>
240    *@param  minimumDimensionValue  the dimension value with which to compare the
241    *      element. The order of dimension values from least to greatest is
242    *      <code>{DONTCARE, TRUE, FALSE, 0, 1, 2}</code>.
243    */
244   public void setAtLeastIfValid(int row, int column, int minimumDimensionValue) {
245     if (row >= 0 && column >= 0) {
246       setAtLeast(row, column, minimumDimensionValue);
247     }
248   }
249  
250   /**
251    *  For each element in this <code>IntersectionMatrix</code>, changes the
252    *  element to the corresponding minimum dimension symbol if the element is
253    *  less.
254    *
255    *@param  minimumDimensionSymbols  nine dimension symbols with which to
256    *      compare the elements of this <code>IntersectionMatrix</code>. The
257    *      order of dimension values from least to greatest is <code>{DONTCARE, TRUE, FALSE, 0, 1, 2}</code>
258    *      .
259    */
260   public void setAtLeast(String minimumDimensionSymbols) {
261     for (int i = 0; i < minimumDimensionSymbols.length(); i++) {
262       int row = i / 3;
263       int col = i % 3;
264       setAtLeast(row, col, Dimension.toDimensionValue(minimumDimensionSymbols.charAt(i)));
265     }
266   }
267  
268   /**
269    *  Changes the elements of this <code>IntersectionMatrix</code> to <code>dimensionValue</code>
270    *  .
271    *
272    *@param  dimensionValue  the dimension value to which to set this <code>IntersectionMatrix</code>
273    *      s elements. Possible values <code>{TRUE, FALSE, DONTCARE, 0, 1, 2}</code>
274    *      .
275    */
276   public void setAll(int dimensionValue) {
277     for (int ai = 0; ai < 3; ai++) {
278       for (int bi = 0; bi < 3; bi++) {
279         matrix[ai][bi] = dimensionValue;
280       }
281     }
282   }
283  
284   /**
285    *  Returns the value of one of this matrix
286    *  entries.
287    *  The value of the provided index is one of the 
288    *  values from the {@link Location} class.  
289    *  The value returned is a constant 
290    *  from the {@link Dimension} class.
291    *
292    *@param  row     the row of this <code>IntersectionMatrix</code>, indicating
293    *      the interior, boundary or exterior of the first <code>Geometry</code>
294    *@param  column  the column of this <code>IntersectionMatrix</code>,
295    *      indicating the interior, boundary or exterior of the second <code>Geometry</code>
296    *@return         the dimension value at the given matrix position.
297    */
298   public int get(int row, int column) {
299     return matrix[row][column];
300   }
301  
302   /**
303    * Tests if this matrix matches <code>[FF*FF****]</code>.
304    *
305    *@return    <code>true</code> if the two <code>Geometry</code>s related by
306    *      this matrix are disjoint
307    */
308   public boolean isDisjoint() {
309     return
310         matrix[Location.INTERIOR][Location.INTERIOR] == Dimension.FALSE &&
311         matrix[Location.INTERIOR][Location.BOUNDARY] == Dimension.FALSE &&
312         matrix[Location.BOUNDARY][Location.INTERIOR] == Dimension.FALSE &&
313         matrix[Location.BOUNDARY][Location.BOUNDARY] == Dimension.FALSE;
314   }
315  
316   /**
317    *  Tests if <code>isDisjoint</code> returns false.
318    *
319    *@return <code>true</code> if the two <code>Geometry</code>s related by
320    *      this matrix intersect
321    */
322   public boolean isIntersects() {
323     return ! isDisjoint();
324   }
325  
326   /**
327    *  Tests if this matrix matches
328    *  <code>[FT*******]</code>, <code>[F**T*****]</code> or <code>[F***T****]</code>.
329    *
330    *@param  dimensionOfGeometryA  the dimension of the first <code>Geometry</code>
331    *@param  dimensionOfGeometryB  the dimension of the second <code>Geometry</code>
332    *@return                       <code>true</code> if the two <code>Geometry</code>
333    *      s related by this matrix touch; Returns false
334    *      if both <code>Geometry</code>s are points.
335    */
336   public boolean isTouches(int dimensionOfGeometryA, int dimensionOfGeometryB) {
337     if (dimensionOfGeometryA > dimensionOfGeometryB) {
338       //no need to get transpose because pattern matrix is symmetrical
339       return isTouches(dimensionOfGeometryB, dimensionOfGeometryA);
340     }
341     if ((dimensionOfGeometryA == Dimension.A && dimensionOfGeometryB == Dimension.A) ||
342         (dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.L) ||
343         (dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.A) ||
344         (dimensionOfGeometryA == Dimension.P && dimensionOfGeometryB == Dimension.A) ||
345         (dimensionOfGeometryA == Dimension.P && dimensionOfGeometryB == Dimension.L)) {
346       return matrix[Location.INTERIOR][Location.INTERIOR] == Dimension.FALSE &&
347           (isTrue(matrix[Location.INTERIOR][Location.BOUNDARY])
348            || isTrue(matrix[Location.BOUNDARY][Location.INTERIOR])
349            || isTrue(matrix[Location.BOUNDARY][Location.BOUNDARY]));
350     }
351     return false;
352   }
353  
354   /**
355    * Tests whether this geometry crosses the
356    * specified geometry.
357    * <p>
358    * The <code>crosses</code> predicate has the following equivalent definitions:
359    * <ul>
360    * <li>The geometries have some but not all interior points in common.
361    * <li>The DE-9IM Intersection Matrix for the two geometries matches
362    *   <ul>
363    *    <li><code>[T*T******]</code> (for P/L, P/A, and L/A situations)
364    *    <li><code>[T*****T**]</code> (for L/P, L/A, and A/L situations)
365    *    <li><code>[0********]</code> (for L/L situations)
366    *   </ul>
367    * </ul>
368    * For any other combination of dimensions this predicate returns <code>false</code>.
369    * <p>
370    * The SFS defined this predicate only for P/L, P/A, L/L, and L/A situations.
371    * JTS extends the definition to apply to L/P, A/P and A/L situations as well.
372    * This makes the relation symmetric.
373    *
374    *@param  dimensionOfGeometryA  the dimension of the first <code>Geometry</code>
375    *@param  dimensionOfGeometryB  the dimension of the second <code>Geometry</code>
376    *@return                       <code>true</code> if the two <code>Geometry</code>s
377    *      related by this matrix cross.
378    */
379   public boolean isCrosses(int dimensionOfGeometryA, int dimensionOfGeometryB) {
380     if ((dimensionOfGeometryA == Dimension.P && dimensionOfGeometryB == Dimension.L) ||
381         (dimensionOfGeometryA == Dimension.P && dimensionOfGeometryB == Dimension.A) ||
382         (dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.A)) {
383       return isTrue(matrix[Location.INTERIOR][Location.INTERIOR]) &&
384       isTrue(matrix[Location.INTERIOR][Location.EXTERIOR]);
385     }
386     if ((dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.P) ||
387         (dimensionOfGeometryA == Dimension.A && dimensionOfGeometryB == Dimension.P) ||
388         (dimensionOfGeometryA == Dimension.A && dimensionOfGeometryB == Dimension.L)) {
389       return isTrue(matrix[Location.INTERIOR][Location.INTERIOR]) &&
390       isTrue(matrix[Location.EXTERIOR][Location.INTERIOR]);
391     }
392     if (dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.L) {
393       return matrix[Location.INTERIOR][Location.INTERIOR] == 0;
394     }
395     return false;
396   }
397  
398   /**
399    * Tests whether this matrix matches <code>[T*F**F***]</code>.
400    *
401    *@return    <code>true</code> if the first <code>Geometry</code> is within
402    *      the second
403    */
404   public boolean isWithin() {
405     return isTrue(matrix[Location.INTERIOR][Location.INTERIOR]) &&
406         matrix[Location.INTERIOR][Location.EXTERIOR] == Dimension.FALSE &&
407         matrix[Location.BOUNDARY][Location.EXTERIOR] == Dimension.FALSE;
408   }
409  
410   /**
411    * Tests whether this matrix matches [T*****FF*[.
412    *
413    *@return    <code>true</code> if the first <code>Geometry</code> contains the
414    *      second
415    */
416   public boolean isContains() {
417     return isTrue(matrix[Location.INTERIOR][Location.INTERIOR]) &&
418         matrix[Location.EXTERIOR][Location.INTERIOR] == Dimension.FALSE &&
419         matrix[Location.EXTERIOR][Location.BOUNDARY] == Dimension.FALSE;
420   }
421  
422   /**
423    * Tests if this matrix matches
424    *    <code>[T*****FF*]</code>
425    * or <code>[*T****FF*]</code>
426    * or <code>[***T**FF*]</code>
427    * or <code>[****T*FF*]</code>
428    *
429    *@return    <code>true</code> if the first <code>Geometry</code> covers the
430    *      second
431    */
432   public boolean isCovers() {
433     boolean hasPointInCommon =
434         isTrue(matrix[Location.INTERIOR][Location.INTERIOR])
435         || isTrue(matrix[Location.INTERIOR][Location.BOUNDARY])
436         || isTrue(matrix[Location.BOUNDARY][Location.INTERIOR])
437         || isTrue(matrix[Location.BOUNDARY][Location.BOUNDARY]);
438  
439     return hasPointInCommon &&
440         matrix[Location.EXTERIOR][Location.INTERIOR] == Dimension.FALSE &&
441         matrix[Location.EXTERIOR][Location.BOUNDARY] == Dimension.FALSE;
442   }
443  
444   /**
445    *Tests if this matrix matches
446    *    <code>[T*F**F***]</code>
447    * or <code>[*TF**F***]</code>
448    * or <code>[**FT*F***]</code>
449    * or <code>[**F*TF***]</code>
450    *
451    *@return    <code>true</code> if the first <code>Geometry</code>
452    * is covered by the second
453    */
454   public boolean isCoveredBy() {
455     boolean hasPointInCommon =
456         isTrue(matrix[Location.INTERIOR][Location.INTERIOR])
457         || isTrue(matrix[Location.INTERIOR][Location.BOUNDARY])
458         || isTrue(matrix[Location.BOUNDARY][Location.INTERIOR])
459         || isTrue(matrix[Location.BOUNDARY][Location.BOUNDARY]);
460  
461     return hasPointInCommon &&
462         matrix[Location.INTERIOR][Location.EXTERIOR] == Dimension.FALSE &&
463         matrix[Location.BOUNDARY][Location.EXTERIOR] == Dimension.FALSE;
464   }
465  
466   /**
467    *  Tests whether the argument dimensions are equal and 
468    *  this matrix matches the pattern <tt>[T*F**FFF*]</tt>.
469    *  <p>
470    *  <b>Note:</b> This pattern differs from the one stated in 
471    *  <i>Simple feature access - Part 1: Common architecture</i>.
472    *  That document states the pattern as <tt>[TFFFTFFFT]</tt>.  This would
473    *  specify that
474    *  two identical <tt>POINT</tt>s are not equal, which is not desirable behaviour.
475    *  The pattern used here has been corrected to compute equality in this situation.
476    *
477    *@param  dimensionOfGeometryA  the dimension of the first <code>Geometry</code>
478    *@param  dimensionOfGeometryB  the dimension of the second <code>Geometry</code>
479    *@return                       <code>true</code> if the two <code>Geometry</code>s
480    *      related by this matrix are equal; the
481    *      <code>Geometry</code>s must have the same dimension to be equal
482    */
483   public boolean isEquals(int dimensionOfGeometryA, int dimensionOfGeometryB) {
484     if (dimensionOfGeometryA != dimensionOfGeometryB) {
485       return false;
486     }
487     return isTrue(matrix[Location.INTERIOR][Location.INTERIOR]) &&
488         matrix[Location.INTERIOR][Location.EXTERIOR] == Dimension.FALSE &&
489         matrix[Location.BOUNDARY][Location.EXTERIOR] == Dimension.FALSE &&
490         matrix[Location.EXTERIOR][Location.INTERIOR] == Dimension.FALSE &&
491         matrix[Location.EXTERIOR][Location.BOUNDARY] == Dimension.FALSE;
492   }
493  
494   /**
495    * Tests if this matrix matches
496    *  <UL>
497    *    <LI><tt>[T*T***T**]</tt> (for two points or two surfaces)
498    *    <LI><tt>[1*T***T**]</tt> (for two curves)
499    *  </UL>.
500    *
501    *@param  dimensionOfGeometryA  the dimension of the first <code>Geometry</code>
502    *@param  dimensionOfGeometryB  the dimension of the second <code>Geometry</code>
503    *@return                       <code>true</code> if the two <code>Geometry</code>s
504    *      related by this matrix overlap. For this
505    *      function to return <code>true</code>, the <code>Geometry</code>s must
506    *      be two points, two curves or two surfaces.
507    */
508   public boolean isOverlaps(int dimensionOfGeometryA, int dimensionOfGeometryB) {
509     if ((dimensionOfGeometryA == Dimension.P && dimensionOfGeometryB == Dimension.P) ||
510         (dimensionOfGeometryA == Dimension.A && dimensionOfGeometryB == Dimension.A)) {
511       return isTrue(matrix[Location.INTERIOR][Location.INTERIOR]) 
512           && isTrue(matrix[Location.INTERIOR][Location.EXTERIOR]) 
513           && isTrue(matrix[Location.EXTERIOR][Location.INTERIOR]);
514     }
515     if (dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.L) {
516       return matrix[Location.INTERIOR][Location.INTERIOR] == 1 
517          && isTrue(matrix[Location.INTERIOR][Location.EXTERIOR]) 
518          && isTrue(matrix[Location.EXTERIOR][Location.INTERIOR]);
519     }
520     return false;
521   }
522  
523   /**
524    * Tests whether this matrix matches the given matrix pattern.
525    *
526    *@param  pattern A pattern containing nine dimension symbols with which to
527    *      compare the entries of this matrix. Possible
528    *      symbol values are <code>{T, F, * , 0, 1, 2}</code>.
529    *@return <code>true</code> if this matrix matches the pattern
530    */
531   public boolean matches(String pattern) {
532     if (pattern.length() != 9) {
533       throw new IllegalArgumentException("Should be length 9: " + pattern);
534     }
535     for (int ai = 0; ai < 3; ai++) {
536       for (int bi = 0; bi < 3; bi++) {
537         if (!matches(matrix[ai][bi], pattern.charAt(3 * ai +
538             bi))) {
539           return false;
540         }
541       }
542     }
543     return true;
544   }
545  
546   /**
547    *  Transposes this IntersectionMatrix.
548    *
549    *@return    this <code>IntersectionMatrix</code> as a convenience
550    */
551   public IntersectionMatrix transpose() {
552     int temp = matrix[1][0];
553     matrix[1][0] = matrix[0][1];
554     matrix[0][1] = temp;
555     temp = matrix[2][0];
556     matrix[2][0] = matrix[0][2];
557     matrix[0][2] = temp;
558     temp = matrix[2][1];
559     matrix[2][1] = matrix[1][2];
560     matrix[1][2] = temp;
561     return this;
562   }
563  
564   /**
565    *  Returns a nine-character <code>String</code> representation of this <code>IntersectionMatrix</code>
566    *  .
567    *
568    *@return    the nine dimension symbols of this <code>IntersectionMatrix</code>
569    *      in row-major order.
570    */
571   public String toString() {
572     StringBuilder builder = new StringBuilder("123456789");
573     for (int ai = 0; ai < 3; ai++) {
574       for (int bi = 0; bi < 3; bi++) {
575         builder.setCharAt(3 * ai + bi, Dimension.toDimensionSymbol(matrix[ai][bi]));
576       }
577     }
578     return builder.toString();
579   }
580 }
581  
582