001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.gce.geotiff.IIOMetadataAdpaters;
017:
018: import java.awt.geom.AffineTransform;
019:
020: import org.geotools.gce.geotiff.IIOMetadataAdpaters.utils.GeoTiffConstants;
021: import org.geotools.util.KeySortedList;
022: import org.jdom.Element;
023:
024: import com.sun.media.imageio.plugins.tiff.BaselineTIFFTagSet;
025: import com.sun.media.imageio.plugins.tiff.GeoTIFFTagSet;
026: import com.sun.media.imageio.plugins.tiff.TIFFTag;
027:
028: /**
029: * This class is responsible for encoding the geotiff tags into suitable
030: * metadata for the ImageIO library.
031: *
032: * <p>
033: * Basically it is and encoder/adapter that collects all the different tags,
034: * order it accordingly to the spec and then organize then into a dom tree ready
035: * to be used by the ImageIO metadata mechanism.
036: *
037: *
038: *
039: * @author Simone Giannecchini
040: * @since 2.3
041: *
042: */
043: public final class GeoTiffIIOMetadataEncoder {
044:
045: private int numModelTiePoints;
046:
047: private TiePoint[] modelTiePoints;
048:
049: private PixelScale modelPixelScale;
050:
051: private double[] modelTransformation;
052:
053: private int numGeoTiffEntries;
054:
055: private KeySortedList geoTiffEntries;
056:
057: private int numGeoTiffDoubleParams;
058:
059: private double[] geoTiffDoubleParams;
060:
061: private int numGeoTiffAsciiParams;
062:
063: private StringBuffer geoTiffAsciiParams;
064:
065: public GeoTiffIIOMetadataEncoder() {
066: this (GeoTiffConstants.DEFAULT_GEOTIFF_VERSION,
067: GeoTiffConstants.DEFAULT_KEY_REVISION_MAJOR,
068: GeoTiffConstants.DEFAULT_KEY_REVISION_MINOR);
069: }
070:
071: public GeoTiffIIOMetadataEncoder(final int geoTIFFVersion,
072: final int keyRevisionMajor, final int keyRevisionMinor) {
073: geoTiffEntries = new KeySortedList();
074: geoTiffDoubleParams = new double[GeoTiffConstants.ARRAY_ELEM_INCREMENT];
075: geoTiffAsciiParams = new StringBuffer();
076: modelTiePoints = new TiePoint[GeoTiffConstants.ARRAY_ELEM_INCREMENT];
077: modelPixelScale = new PixelScale();
078: modelTransformation = new double[16];
079: addGeoKeyEntry(geoTIFFVersion, keyRevisionMajor, 1,
080: keyRevisionMinor);
081: }
082:
083: public static boolean isTiffUShort(final int value) {
084: return (value >= GeoTiffConstants.USHORT_MIN)
085: && (value <= GeoTiffConstants.USHORT_MAX);
086: }
087:
088: public int getGeoTIFFVersion() {
089: return getGeoKeyEntryAt(0).getKeyID();
090: }
091:
092: public void setGeoTIFFVersion(int version) {
093: getGeoKeyEntryAt(0).setKeyID(version);
094: }
095:
096: public int getKeyRevisionMajor() {
097: return getGeoKeyEntryAt(0).getTiffTagLocation();
098: }
099:
100: public int getKeyRevisionMinor() {
101: return getGeoKeyEntryAt(0).getCount();
102: }
103:
104: public void setKeyRevision(int major, int minor) {
105: getGeoKeyEntryAt(0).setTiffTagLocation(major);
106: getGeoKeyEntryAt(0).setCount(minor);
107: }
108:
109: public double getModelPixelScaleX() {
110: return modelPixelScale.getScaleX();
111: }
112:
113: public double getModelPixelScaleY() {
114: return modelPixelScale.getScaleY();
115: }
116:
117: public double getModelPixelScaleZ() {
118: return modelPixelScale.getScaleZ();
119: }
120:
121: public void setModelPixelScale(double x, double y) {
122: setModelPixelScale(x, y, 0.0);
123: }
124:
125: public void setModelPixelScale(double x, double y, double z) {
126: if (isModelTransformationSet())
127: throw new IllegalStateException(
128: "ModelTransformationTag already set. It is not possible to set the ModelPixelScale.");
129: modelPixelScale.setScaleX(x);
130: modelPixelScale.setScaleY(y);
131: modelPixelScale.setScaleZ(z);
132: }
133:
134: public int getNumModelTiePoints() {
135: return numModelTiePoints;
136: }
137:
138: public TiePoint getModelTiePoint() {
139: return getModelTiePointAt(0);
140: }
141:
142: public TiePoint getModelTiePointAt(int index) {
143: return modelTiePoints[index];
144: }
145:
146: public void setModelTiePoint(double i, double j, double x, double y) {
147: setModelTiePoint(i, j, 0.0, x, y, 0.0);
148: }
149:
150: public void setModelTiePoint(double i, double j, double k,
151: double x, double y, double z) {
152: if (isModelTransformationSet())
153: throw new IllegalStateException(
154: "ModelTransformationTag already set. It is not possible to set the ModelTiePoint.");
155: if (getNumModelTiePoints() > 0) {
156: getModelTiePointAt(0).set(i, j, k, x, y, z);
157: } else {
158: addModelTiePoint(i, j, k, x, y, z);
159: }
160: }
161:
162: public void addModelTiePoint(double i, double j, double x, double y) {
163: addModelTiePoint(i, j, 0.0, x, y, 0.0);
164: }
165:
166: public void addModelTiePoint(double i, double j, double k,
167: double x, double y, double z) {
168: final int numTiePoints = numModelTiePoints;
169:
170: if (numTiePoints >= (modelTiePoints.length - 1)) {
171: final TiePoint[] tiePoints = new TiePoint[numTiePoints
172: + GeoTiffConstants.ARRAY_ELEM_INCREMENT];
173: System.arraycopy(modelTiePoints, 0, tiePoints, 0,
174: numTiePoints);
175: modelTiePoints = tiePoints;
176: }
177:
178: modelTiePoints[numTiePoints] = new TiePoint(i, j, k, x, y, z);
179: numModelTiePoints++;
180: }
181:
182: public int getNumGeoKeyEntries() {
183: return numGeoTiffEntries;
184: }
185:
186: public GeoKeyEntry getGeoKeyEntryAt(int index) {
187: // got to retrieve the eleme at a certain index
188: final Object it = this .geoTiffEntries.get(index);
189: if (it != null)
190: return (GeoKeyEntry) (it);
191: return null;
192: }
193:
194: public GeoKeyEntry getGeoKeyEntry(int keyID) {
195: GeoKeyEntry retVal = null;
196: final Object o = geoTiffEntries.first(new Integer(keyID));
197: if (o != null)
198: retVal = (GeoKeyEntry) o;
199:
200: return retVal;
201: }
202:
203: public boolean hasGeoKeyEntry(int keyID) {
204: return getGeoKeyEntry(keyID) != null;
205: }
206:
207: public int getGeoShortParam(int keyID) {
208: final GeoKeyEntry entry = getNonNullKeyEntry(keyID);
209: final int tag = entry.getTiffTagLocation();
210: final int value = entry.getValueOffset();
211: checkParamTag(tag, 0);
212:
213: return value;
214: }
215:
216: public double getGeoDoubleParam(int keyID) {
217: final GeoKeyEntry entry = getNonNullKeyEntry(keyID);
218: final int tag = entry.getTiffTagLocation();
219: final int offset = entry.getValueOffset();
220: checkParamTag(tag, getGeoDoubleParamsTag().getNumber());
221:
222: return geoTiffDoubleParams[offset];
223: }
224:
225: public double[] getGeoDoubleParams(int keyID) {
226: return getGeoDoubleParams(keyID, null);
227: }
228:
229: public double[] getGeoDoubleParams(int keyID, double[] values) {
230: final GeoKeyEntry entry = getNonNullKeyEntry(keyID);
231: final int tag = entry.getTiffTagLocation();
232: final int count = entry.getCount();
233: final int offset = entry.getValueOffset();
234: checkParamTag(tag, getGeoDoubleParamsTag().getNumber());
235:
236: if (values == null) {
237: values = new double[count];
238: }
239:
240: System.arraycopy(geoTiffDoubleParams, offset, values, 0, count);
241:
242: return values;
243: }
244:
245: public String getGeoAsciiParam(int keyID) {
246: final GeoKeyEntry entry = getNonNullKeyEntry(keyID);
247: final int tag = entry.getTiffTagLocation();
248: final int count = entry.getCount();
249: final int offset = entry.getValueOffset();
250: checkParamTag(tag, getGeoAsciiParamsTag().getNumber());
251:
252: return geoTiffAsciiParams.substring(offset,
253: (offset + count) - 1);
254: }
255:
256: public void addGeoShortParam(int keyID, int value) {
257: addGeoKeyEntry(keyID, 0, 1, value);
258: }
259:
260: public void addGeoDoubleParam(int keyID, double value) {
261: addGeoDoubleParamsRef(keyID, 1);
262: addDoubleParam(value);
263: }
264:
265: public void addGeoDoubleParams(int keyID, double[] values) {
266: addGeoDoubleParamsRef(keyID, values.length);
267: final int length = values.length;
268: for (int i = 0; i < length; i++) {
269: addDoubleParam(values[i]);
270: }
271: }
272:
273: public void addGeoAscii(int keyID, String value) {
274: addGeoAsciiParamsRef(keyID, value.length() + 1);
275: // +1 for the '|' character to be appended
276: addAsciiParam(value);
277: }
278:
279: private void addGeoKeyEntry(int keyID, int tag, int count,
280: int offset) {
281: if (!isTiffUShort(keyID)) {
282: throw new IllegalArgumentException(
283: "keyID is not a TIFF USHORT");
284: }
285:
286: if (!isTiffUShort(tag)) {
287: throw new IllegalArgumentException(
288: "tag is not a TIFF USHORT");
289: }
290:
291: if (!isTiffUShort(count)) {
292: throw new IllegalArgumentException(
293: "count is not a TIFF USHORT");
294: }
295:
296: if (!isTiffUShort(offset)) {
297: throw new IllegalArgumentException(
298: "offset is not a TIFF USHORT");
299: }
300:
301: final int numKeyEntries = numGeoTiffEntries;
302: geoTiffEntries.add(new Integer(keyID), new GeoKeyEntry(keyID,
303: tag, offset, count));
304: getGeoKeyEntryAt(0).setCount(numKeyEntries);
305: numGeoTiffEntries++;
306: }
307:
308: public void assignTo(Element element) {
309: if (!element.getName().equals(
310: GeoTiffConstants.GEOTIFF_IIO_ROOT_ELEMENT_NAME)) {
311: throw new IllegalArgumentException("root not found: "
312: + GeoTiffConstants.GEOTIFF_IIO_ROOT_ELEMENT_NAME);
313: }
314:
315: final Element ifd1 = element
316: .getChild(GeoTiffConstants.GEOTIFF_IFD_TAG);
317:
318: if (ifd1 == null) {
319: throw new IllegalArgumentException("Unable to find child "
320: + GeoTiffConstants.GEOTIFF_IFD_TAG);
321: }
322:
323: final Element ifd2 = createIFD();
324: ifd1
325: .setAttribute(
326: GeoTiffConstants.GEOTIFF_TAGSETS_ATT_NAME,
327: ifd2
328: .getAttributeValue(GeoTiffConstants.GEOTIFF_TAGSETS_ATT_NAME));
329:
330: final Element[] childElems = (Element[]) ifd2.getChildren()
331: .toArray(new Element[0]);
332: final int length = childElems.length;
333: Element child;
334: for (int i = 0; i < length; i++) {
335: child = childElems[i];
336: ifd2.removeContent(child);
337: ifd1.addContent(child);
338: }
339: }
340:
341: public Element createRootTree() {
342: final Element rootElement = new Element(
343: GeoTiffConstants.GEOTIFF_IIO_ROOT_ELEMENT_NAME);
344: rootElement.addContent(createIFD());
345:
346: return rootElement;
347: }
348:
349: protected static TIFFTag getGeoKeyDirectoryTag() {
350: return GeoTIFFTagSet.getInstance().getTag(
351: GeoTIFFTagSet.TAG_GEO_KEY_DIRECTORY);
352: }
353:
354: protected static TIFFTag getGeoDoubleParamsTag() {
355: return GeoTIFFTagSet.getInstance().getTag(
356: GeoTIFFTagSet.TAG_GEO_DOUBLE_PARAMS);
357: }
358:
359: protected static TIFFTag getGeoAsciiParamsTag() {
360: return GeoTIFFTagSet.getInstance().getTag(
361: GeoTIFFTagSet.TAG_GEO_ASCII_PARAMS);
362: }
363:
364: protected static TIFFTag getModelPixelScaleTag() {
365: return GeoTIFFTagSet.getInstance().getTag(
366: GeoTIFFTagSet.TAG_MODEL_PIXEL_SCALE);
367: }
368:
369: protected static TIFFTag getModelTiePointTag() {
370: return GeoTIFFTagSet.getInstance().getTag(
371: GeoTIFFTagSet.TAG_MODEL_TIE_POINT);
372: }
373:
374: protected static TIFFTag getModelTransformationTag() {
375: return GeoTIFFTagSet.getInstance().getTag(
376: GeoTIFFTagSet.TAG_MODEL_TRANSFORMATION);
377: }
378:
379: private GeoKeyEntry getNonNullKeyEntry(int keyID) {
380: final GeoKeyEntry entry = getGeoKeyEntry(keyID);
381:
382: if (entry == null) {
383: throw new IllegalArgumentException(
384: "Unable to find an entry for the provided geo key "
385: + keyID);
386: }
387:
388: return entry;
389: }
390:
391: private void checkParamTag(final int tag, final int expectedTag) {
392: if (tag != expectedTag) {
393: if (expectedTag == 0) {
394: throw new IllegalArgumentException(
395: "invalid key access, not a GeoTIFF SHORT parameter");
396: } else if (expectedTag == getGeoDoubleParamsTag()
397: .getNumber()) {
398: throw new IllegalArgumentException(
399: "invalid key access, not a GeoTIFF DOUBLE parameter");
400: } else if (expectedTag == getGeoAsciiParamsTag()
401: .getNumber()) {
402: throw new IllegalArgumentException(
403: "invalid key access, not a GeoTIFF ASCII parameter");
404: } else {
405: throw new IllegalStateException();
406: }
407: }
408: }
409:
410: private void addDoubleParam(double param) {
411: final int numDoubleParams = numGeoTiffDoubleParams;
412:
413: if (numDoubleParams >= (geoTiffDoubleParams.length - 1)) {
414: final double[] doubleParams = new double[numDoubleParams
415: + GeoTiffConstants.ARRAY_ELEM_INCREMENT];
416: System.arraycopy(geoTiffDoubleParams, 0, doubleParams, 0,
417: numDoubleParams);
418: geoTiffDoubleParams = doubleParams;
419: }
420:
421: geoTiffDoubleParams[numDoubleParams] = param;
422: numGeoTiffDoubleParams++;
423: }
424:
425: private void addAsciiParam(String param) {
426: geoTiffAsciiParams.append(param);
427: geoTiffAsciiParams.append('|');
428: numGeoTiffAsciiParams++;
429: }
430:
431: private void addGeoDoubleParamsRef(int keyID, int count) {
432: addGeoKeyEntry(keyID, getGeoDoubleParamsTag().getNumber(),
433: count, getCurrentGeoDoublesOffset());
434: }
435:
436: private void addGeoAsciiParamsRef(int keyID, int length) {
437: addGeoKeyEntry(keyID, getGeoAsciiParamsTag().getNumber(),
438: length, getCurrentGeoAsciisOffset());
439: }
440:
441: private int getCurrentGeoDoublesOffset() {
442: return numGeoTiffDoubleParams;
443: }
444:
445: private int getCurrentGeoAsciisOffset() {
446: return geoTiffAsciiParams.length();
447: }
448:
449: private Element createIFD() {
450: Element ifd = new Element(GeoTiffConstants.GEOTIFF_IFD_TAG);
451: ifd.setAttribute(GeoTiffConstants.GEOTIFF_TAGSETS_ATT_NAME,
452: BaselineTIFFTagSet.class.getName() + ","
453: + GeoTIFFTagSet.class.getName());
454:
455: if (modelPixelScale.isSet()) {
456: ifd.addContent(createModelPixelScaleElement());
457: }
458:
459: if (isModelTiePointsSet()) {
460: ifd.addContent(createModelTiePointsElement());
461: } else if (isModelTransformationSet()) {
462: ifd.addContent(createModelTransformationElement());
463: }
464:
465: if (getNumGeoKeyEntries() > 1) {
466: ifd.addContent(createGeoKeyDirectoryElement());
467: }
468:
469: if (numGeoTiffDoubleParams > 0) {
470: ifd.addContent(createGeoDoubleParamsElement());
471: }
472:
473: if (numGeoTiffAsciiParams > 0) {
474: ifd.addContent(createGeoAsciiParamsElement());
475: }
476:
477: return ifd;
478: }
479:
480: private boolean isModelTiePointsSet() {
481: return numModelTiePoints > 0;
482: }
483:
484: private boolean isModelTransformationSet() {
485: final int length = modelTransformation.length;
486: for (int i = 0; i < length; i++) {
487: if (modelTransformation[i] != 0.0) {
488: return true;
489: }
490: }
491:
492: return false;
493: }
494:
495: public void setModelTransformation(
496: final AffineTransform rasterToModel) {
497: if (modelPixelScale != null && modelPixelScale.isSet())
498: throw new IllegalStateException(
499: "ModelPixelScaleTag already set. It is not possible to set the ModelTransformation.");
500: if (isModelTiePointsSet())
501: throw new IllegalStateException(
502: "ModelTiePointsTag already set. It is not possible to set the ModelTransformation.");
503:
504: // //
505: //
506: // See pag 28 of the spec for an explanation
507: //
508: // //
509: // a
510: modelTransformation[0] = rasterToModel.getScaleX();
511: // b
512: modelTransformation[1] = rasterToModel.getShearX();
513: // c
514: modelTransformation[2] = 0;
515: // d
516: modelTransformation[3] = rasterToModel.getTranslateX();
517: // e
518: modelTransformation[4] = rasterToModel.getShearY();
519: // f
520: modelTransformation[5] = rasterToModel.getScaleY();
521: // g
522: modelTransformation[6] = 0;
523: // h
524: modelTransformation[7] = rasterToModel.getTranslateY();
525: // i
526: modelTransformation[8] = 0;
527: // j
528: modelTransformation[9] = 0;
529: // k
530: modelTransformation[10] = 0;
531: // l
532: modelTransformation[11] = 0;
533: // m
534: modelTransformation[12] = 0;
535: // n
536: modelTransformation[13] = 0;
537: // o
538: modelTransformation[14] = 0;
539: // p
540: modelTransformation[15] = 1;
541:
542: }
543:
544: private Element createGeoKeyDirectoryElement() {
545: Element field = createFieldElement(getGeoKeyDirectoryTag());
546: Element data = new Element(GeoTiffConstants.GEOTIFF_SHORTS_TAG);
547: field.addContent(data);
548: int[] values;
549: int lenght;
550: Element GeoKeyRecord;
551: ;
552: for (int i = 0; i < numGeoTiffEntries; i++) {
553: values = getGeoKeyEntryAt(i).getValues();
554: lenght = values.length;
555: for (int j = 0; j < lenght; j++) {
556: GeoKeyRecord = createShortElement(values[j]);
557: data.addContent(GeoKeyRecord);
558: }
559: }
560:
561: return field;
562: }
563:
564: private Element createGeoDoubleParamsElement() {
565: Element field = createFieldElement(getGeoDoubleParamsTag());
566: Element data = new Element(GeoTiffConstants.GEOTIFF_DOUBLES_TAG);
567: field.addContent(data);
568: Element param;
569: for (int i = 0; i < numGeoTiffDoubleParams; i++) {
570: param = createDoubleElement(geoTiffDoubleParams[i]);
571: data.addContent(param);
572: }
573:
574: return field;
575: }
576:
577: private Element createGeoAsciiParamsElement() {
578: Element field = createFieldElement(getGeoAsciiParamsTag());
579: Element data = new Element(GeoTiffConstants.GEOTIFF_ASCIIS_TAG);
580: field.addContent(data);
581: data.addContent(createAsciiElement(geoTiffAsciiParams
582: .toString()));
583:
584: return field;
585: }
586:
587: private Element createModelPixelScaleElement() {
588: Element field = createFieldElement(getModelPixelScaleTag());
589: Element data = new Element(GeoTiffConstants.GEOTIFF_DOUBLES_TAG);
590: field.addContent(data);
591: addDoubleElements(data, modelPixelScale.getValues());
592:
593: return field;
594: }
595:
596: private Element createModelTransformationElement() {
597: Element field = createFieldElement(getModelTransformationTag());
598: Element data = new Element(GeoTiffConstants.GEOTIFF_DOUBLES_TAG);
599: field.addContent(data);
600: addDoubleElements(data, modelTransformation);
601:
602: return field;
603: }
604:
605: private Element createModelTiePointsElement() {
606: Element field = createFieldElement(getModelTiePointTag());
607: Element data = new Element(GeoTiffConstants.GEOTIFF_DOUBLES_TAG);
608: field.addContent(data);
609:
610: for (int i = 0; i < numModelTiePoints; i++) {
611: addDoubleElements(data, modelTiePoints[i].getData());
612: }
613:
614: return field;
615: }
616:
617: private Element createFieldElement(final TIFFTag tag) {
618: Element field = new Element(GeoTiffConstants.GEOTIFF_FIELD_TAG);
619: field.setAttribute(GeoTiffConstants.NUMBER_ATTR, String
620: .valueOf(tag.getNumber()));
621: field.setAttribute(GeoTiffConstants.NAME_ATTR, tag.getName());
622:
623: return field;
624: }
625:
626: private Element createShortElement(final int value) {
627: Element GeoKeyRecord = new Element(
628: GeoTiffConstants.GEOTIFF_SHORT_TAG);
629: GeoKeyRecord.setAttribute(GeoTiffConstants.VALUE_ATTR, String
630: .valueOf(value));
631:
632: return GeoKeyRecord;
633: }
634:
635: private Element createDoubleElement(final double value) {
636: Element param = new Element(GeoTiffConstants.GEOTIFF_DOUBLE_TAG);
637: param.setAttribute(GeoTiffConstants.VALUE_ATTR, String
638: .valueOf(value));
639:
640: return param;
641: }
642:
643: private Element createAsciiElement(final String value) {
644: Element param = new Element(GeoTiffConstants.GEOTIFF_ASCII_TAG);
645: param.setAttribute(GeoTiffConstants.VALUE_ATTR, String
646: .valueOf(value));
647:
648: return param;
649: }
650:
651: private void addDoubleElements(Element data, final double[] values) {
652: final int length = values.length;
653: Element GeoKeyRecord;
654: for (int j = 0; j < length; j++) {
655: GeoKeyRecord = createDoubleElement(values[j]);
656: data.addContent(GeoKeyRecord);
657: }
658: }
659: }
|