Source Code Cross Referenced for CroppedCoverage2D.java in  » GIS » GeoTools-2.4.1 » org » geotools » coverage » processing » operation » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » GIS » GeoTools 2.4.1 » org.geotools.coverage.processing.operation 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         *    GeoTools - OpenSource mapping toolkit
003:         *    http://geotools.org
004:         *    (C) 2006-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; either
009:         *    version 2.1 of the License, or (at your option) any later version.
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.coverage.processing.operation;
017:
018:        import java.awt.RenderingHints;
019:        import java.awt.geom.AffineTransform;
020:        import java.awt.geom.NoninvertibleTransformException;
021:        import java.awt.geom.Point2D;
022:        import java.awt.geom.Rectangle2D;
023:        import java.awt.image.RenderedImage;
024:        import java.awt.image.renderable.ParameterBlock;
025:        import java.util.ArrayList;
026:        import java.util.Collections;
027:        import java.util.List;
028:
029:        import javax.media.jai.ImageLayout;
030:        import javax.media.jai.Interpolation;
031:        import javax.media.jai.JAI;
032:        import javax.media.jai.PlanarImage;
033:        import javax.media.jai.ROI;
034:        import javax.media.jai.ROIShape;
035:        import javax.media.jai.operator.MosaicDescriptor;
036:
037:        import org.geotools.coverage.GridSampleDimension;
038:        import org.geotools.coverage.grid.GeneralGridRange;
039:        import org.geotools.coverage.grid.GridCoverage2D;
040:        import org.geotools.coverage.grid.GridGeometry2D;
041:        import org.geotools.coverage.processing.CannotCropException;
042:        import org.geotools.coverage.processing.OperationJAI;
043:        import org.geotools.factory.GeoTools;
044:        import org.geotools.factory.Hints;
045:        import org.geotools.geometry.Envelope2D;
046:        import org.geotools.geometry.GeneralEnvelope;
047:        import org.geotools.geometry.jts.ReferencedEnvelope;
048:        import org.geotools.referencing.operation.matrix.XAffineTransform;
049:        import org.geotools.referencing.operation.transform.ProjectiveTransform;
050:        import org.geotools.resources.coverage.CoverageUtilities;
051:        import org.geotools.resources.coverage.FeatureUtilities;
052:        import org.geotools.resources.geometry.XRectangle2D;
053:        import org.geotools.resources.i18n.ErrorKeys;
054:        import org.geotools.resources.i18n.Errors;
055:        import org.geotools.resources.image.ImageUtilities;
056:        import org.opengis.coverage.grid.GridCoverage;
057:        import org.opengis.coverage.grid.GridRange;
058:        import org.opengis.metadata.spatial.PixelOrientation;
059:        import org.opengis.parameter.ParameterValueGroup;
060:        import org.opengis.referencing.operation.TransformException;
061:        import org.opengis.util.InternationalString;
062:
063:        import com.vividsolutions.jts.geom.Coordinate;
064:        import com.vividsolutions.jts.geom.GeometryFactory;
065:        import com.vividsolutions.jts.geom.LinearRing;
066:        import com.vividsolutions.jts.geom.Polygon;
067:        import com.vividsolutions.jts.geom.PrecisionModel;
068:
069:        /**
070:         * This class is responsible for applying a crop operation to a source coverage
071:         * with a specified envelope.
072:         * 
073:         * @author Simone Giannecchini, GeoSolutions
074:         * 
075:         */
076:        final class CroppedCoverage2D extends GridCoverage2D {
077:            /**
078:             * 
079:             */
080:            private static final long serialVersionUID = -501742139906901754L;
081:
082:            private final static PrecisionModel pm;
083:
084:            private final static GeometryFactory gf;
085:            static {
086:                // getting default hints
087:                final Hints defaultHints = GeoTools.getDefaultHints();
088:
089:                // check if someone asked us to use a specific precision model
090:                final Object o = defaultHints.get(Hints.JTS_PRECISION_MODEL);
091:                if (o != null)
092:                    pm = (PrecisionModel) o;
093:                else {
094:                    pm = new PrecisionModel();
095:                }
096:                gf = new GeometryFactory(pm, 0);
097:
098:            }
099:
100:            /**
101:             * Convenience constructor for a {@link CroppedCoverage2D}.
102:             * 
103:             * @param name
104:             *            for this {@link GridCoverage2D}.
105:             * @param sourceRaster
106:             *            is the raster that will be the back-end for this
107:             *            {@link GridCoverage2D}.
108:             * @param croppedGeometry
109:             *            is the {@link GridGeometry2D} for this new
110:             *            {@link CroppedCoverage2D}.
111:             * @param source
112:             *            is the original {@link GridCoverage2D}.
113:             * @param actionTaken
114:             *            it is used to do the necessary postprocessing for supporting
115:             *            paletted images.
116:             * @param rasterSpaceROI
117:             *            in case we used the JAI's mosaic with a ROI this
118:             *            {@link java.awt.Polygon} will hold the used roi.
119:             */
120:            private CroppedCoverage2D(InternationalString name,
121:                    PlanarImage sourceRaster, GridGeometry2D croppedGeometry,
122:                    GridCoverage2D source, int actionTaken,
123:                    java.awt.Polygon rasterSpaceROI) {
124:                super (name.toString(), sourceRaster, croppedGeometry,
125:                        (GridSampleDimension[]) (actionTaken == 1 ? null
126:                                : source.getSampleDimensions().clone()),
127:                        new GridCoverage[] { source },
128:                        rasterSpaceROI != null ? Collections.singletonMap(
129:                                "GC_ROI", rasterSpaceROI) : null);
130:            }
131:
132:            /**
133:             * Applies the band select operation to a grid coverage.
134:             * 
135:             * @param parameters
136:             *            List of name value pairs for the parameters.
137:             * @param sourceCoverage
138:             *            is the source {@link GridCoverage2D} that we want to crop.
139:             * @param hints
140:             *            A set of rendering hints, or {@code null} if none.
141:             * @param sourceGridToWorldTransform
142:             *            is the 2d grid-to-world transform for the source coverage.
143:             * @param scaleFactor
144:             *            for the grid-to-world transform.
145:             * @return The result as a grid coverage.
146:             */
147:            static GridCoverage2D create(final ParameterValueGroup parameters,
148:                    RenderingHints hints, GridCoverage2D sourceCoverage,
149:                    AffineTransform sourceGridToWorldTransform,
150:                    double scaleFactor) {
151:
152:                // /////////////////////////////////////////////////////////////////////
153:                //
154:                // Getting the source coverage and its child geolocation objects
155:                //
156:                // /////////////////////////////////////////////////////////////////////
157:                final RenderedImage sourceImage = sourceCoverage
158:                        .getRenderedImage();
159:                final Envelope2D sourceEnvelope = sourceCoverage
160:                        .getEnvelope2D();
161:                final GridGeometry2D sourceGridGeometry = ((GridGeometry2D) sourceCoverage
162:                        .getGridGeometry());
163:                final GeneralGridRange sourceGridRange = (GeneralGridRange) sourceGridGeometry
164:                        .getGridRange();
165:                final double rotationEsteem = XAffineTransform
166:                        .getRotation(sourceGridToWorldTransform);
167:
168:                // /////////////////////////////////////////////////////////////////////
169:                //
170:                // Now we try to understand if we have a simple scale and translate or a
171:                // more elaborated grid-to-world transformation n which case a simple
172:                // crop could not be enough, but we may need a more elaborated chain of
173:                // operation in order to do a good job.
174:                //
175:                // /////////////////////////////////////////////////////////////////////
176:                boolean isSimpleTransform = false;
177:                // check if there is a valid rotation value (it could be 0!)
178:                if (!Double.isNaN(rotationEsteem)) {
179:                    //XXX make this parametric somehow
180:                    if (Math.abs(rotationEsteem) < 1E-3)
181:                        // there is no rotation
182:                        isSimpleTransform = true;
183:                }
184:
185:                // /////////////////////////////////////////////////////////////////////
186:                //
187:                // Managing Hints, especially for output coverage's layout purposes
188:                //
189:                // /////////////////////////////////////////////////////////////////////
190:                RenderingHints targetHints = ImageUtilities
191:                        .getRenderingHints(sourceImage);
192:                if (targetHints == null) {
193:                    targetHints = new RenderingHints(hints);
194:                } else if (hints != null) {
195:                    targetHints.add(hints);
196:                }
197:                // /////////////////////////////////////////////////////////////////////
198:                //
199:                // Interpolation
200:                //
201:                // /////////////////////////////////////////////////////////////////////
202:                Interpolation interpolation = (Interpolation) targetHints
203:                        .get(JAI.KEY_INTERPOLATION);
204:                if (interpolation == null)
205:                    interpolation = (Interpolation) ImageUtilities.NN_INTERPOLATION_HINT
206:                            .get(JAI.KEY_INTERPOLATION);
207:
208:                // /////////////////////////////////////////////////////////////////////
209:                //
210:                // Do we need to explode the Palette to RGB(A)?
211:                //
212:                // /////////////////////////////////////////////////////////////////////
213:                int actionTaken = 0;
214:
215:                // //
216:                //
217:                // Layout
218:                //
219:                // //
220:                ImageLayout layout = (ImageLayout) targetHints
221:                        .get(JAI.KEY_IMAGE_LAYOUT);
222:                if (layout != null) {
223:                    layout = (ImageLayout) layout.clone();
224:                } else {
225:                    layout = new ImageLayout(sourceImage);
226:                    layout.unsetTileLayout();
227:                    // At this point, only the color model and sample model are left
228:                    // valids.
229:                }
230:                // crop will ignore minx, miny width and height
231:                if ((layout.getValidMask() & (ImageLayout.TILE_WIDTH_MASK
232:                        | ImageLayout.TILE_HEIGHT_MASK
233:                        | ImageLayout.TILE_GRID_X_OFFSET_MASK | ImageLayout.TILE_GRID_Y_OFFSET_MASK)) == 0) {
234:                    layout.setTileGridXOffset(layout.getMinX(sourceImage));
235:                    layout.setTileGridYOffset(layout.getMinY(sourceImage));
236:                    final int width = layout.getWidth(sourceImage);
237:                    final int height = layout.getHeight(sourceImage);
238:                    if (layout.getTileWidth(sourceImage) > width)
239:                        layout.setTileWidth(width);
240:                    if (layout.getTileHeight(sourceImage) > height)
241:                        layout.setTileHeight(height);
242:                }
243:                targetHints.put(JAI.KEY_IMAGE_LAYOUT, layout);
244:
245:                // /////////////////////////////////////////////////////////////////////
246:                //
247:                // prepare the processor to use for this operation
248:                //
249:                // /////////////////////////////////////////////////////////////////////
250:                final JAI processor = OperationJAI.getJAI(targetHints);
251:                final boolean useProvidedProcessor = !processor.equals(JAI
252:                        .getDefaultInstance());
253:
254:                try {
255:                    // /////////////////////////////////////////////////////////////////////
256:                    //
257:                    // Get the crop envelope and do your thing!
258:                    //
259:                    // /////////////////////////////////////////////////////////////////////
260:                    final GeneralEnvelope cropEnvelope = (GeneralEnvelope) parameters
261:                            .parameter("Envelope").getValue();
262:                    // should we conserve the crop envelope?
263:                    final Boolean conserveEnvelope = (Boolean) parameters
264:                            .parameter("ConserveEnvelope").getValue();
265:
266:                    // ////////////////////////////////////////////////////////////////////
267:                    //
268:                    // Do we actually need to crop?
269:                    //
270:                    // If the intersecton envelope is empty or if the intersection
271:                    // envelope is (almost) the same of the original envelope we just
272:                    // return (with different return values).
273:                    //
274:                    // ////////////////////////////////////////////////////////////////////
275:                    if (cropEnvelope.isEmpty())
276:                        throw new CannotCropException(Errors
277:                                .format(ErrorKeys.CANT_CROP));
278:                    if (cropEnvelope.equals(sourceEnvelope, scaleFactor / 2.0,
279:                            false))
280:                        return sourceCoverage;
281:
282:                    // //
283:                    //
284:                    // build the new range by keeping into
285:                    // account translation of grid geometry constructor for respecting
286:                    // OGC PIXEL-IS-CENTER ImageDatum assumption.
287:                    //
288:                    // //
289:                    final AffineTransform sourceWorldToGridTransform = sourceGridToWorldTransform
290:                            .createInverse();
291:                    // finalGridRange will hold the rectangular crop area at the end of
292:                    // this operation
293:                    final Rectangle2D finalGridRange = XAffineTransform
294:                            .transform(sourceWorldToGridTransform, cropEnvelope
295:                                    .toRectangle2D(), null);
296:                    // intersection with the original range in order to not try to crop
297:                    // outside the image bounds
298:                    XRectangle2D.intersect(finalGridRange, sourceGridRange
299:                            .toRectangle(), finalGridRange);
300:
301:                    // ////////////////////////////////////////////////////////////////////
302:                    //
303:                    //
304:                    // It is worth to point out that doing a crop the G2W transform
305:                    // should not change while the envelope might change a little bit as
306:                    // a consequence of the roundings of the underlying image datum
307:                    // which uses integer factors. This can be avoided using the
308:                    // conserveEnvelope param. If the user does not explicitly asks to
309:                    // conserve the crop envelope we will conserve the original
310:                    // grid-to-world transform.
311:                    //
312:                    // ////////////////////////////////////////////////////////////////////
313:                    final GeneralGridRange newRange = new GeneralGridRange(
314:                            new GeneralEnvelope(finalGridRange));
315:                    // we do not have to crop in this case (should not really happen at
316:                    // this time)
317:                    if (newRange.equals(sourceGridRange) && isSimpleTransform)
318:                        return sourceCoverage;
319:
320:                    // ////////////////////////////////////////////////////////////////////
321:                    //
322:                    // if I get here I have something to crop
323:                    // using the world-to-grid transform for going from envelope to the
324:                    // new grid range.
325:                    //
326:                    // ////////////////////////////////////////////////////////////////////
327:                    final int xAxis = sourceGridGeometry.gridDimensionX;
328:                    final int yAxis = sourceGridGeometry.gridDimensionY;
329:                    final double minX = newRange.getLower(xAxis);
330:                    final double minY = newRange.getLower(yAxis);
331:                    final double width = newRange.getLength(xAxis);
332:                    final double height = newRange.getLength(yAxis);
333:                    assert width > 0;
334:                    assert height > 0;
335:
336:                    // /////////////////////////////////////////////////////////////////////
337:                    //
338:                    // get the rendered image and crop it
339:                    //
340:                    // /////////////////////////////////////////////////////////////////////
341:                    final PlanarImage croppedImage;
342:                    final ParameterBlock pbj = new ParameterBlock();
343:                    pbj.addSource(sourceImage);
344:                    java.awt.Polygon rasterSpaceROI = null;
345:                    if (isSimpleTransform) {
346:
347:                        // /////////////////////////////////////////////////////////////////////
348:                        //
349:                        // We got a simple scale and translate transform, JAI crop will
350:                        // suffice.
351:                        //
352:                        // /////////////////////////////////////////////////////////////////////
353:                        // executing the crop
354:                        pbj.add(new Float(minX));
355:                        pbj.add(new Float(minY));
356:                        pbj.add(new Float(width));
357:                        pbj.add(new Float(height));
358:                        if (!useProvidedProcessor)
359:                            croppedImage = JAI.create("Crop", pbj, targetHints);
360:                        else
361:                            croppedImage = processor.createNS("Crop", pbj,
362:                                    targetHints);
363:                    } else {
364:                        // /////////////////////////////////////////////////////////////////////
365:                        //
366:                        // We don't have a simple scale and translate transform, JAI
367:                        // crop MAY NOT suffice. Let's decide whether or not we'll use
368:                        // the Mosaic.
369:                        //
370:                        // /////////////////////////////////////////////////////////////////////
371:                        // //
372:                        //
373:                        // Convert the crop envelope into a polygon and the use the
374:                        // world-to-grid transform to get a ROI for the source coverage.
375:                        //
376:                        // //
377:                        final Rectangle2D rect = cropEnvelope.toRectangle2D();
378:                        final Coordinate[] coord = new Coordinate[] {
379:                                new Coordinate(rect.getMinX(), rect.getMinY()),
380:                                new Coordinate(rect.getMinX(), rect.getMaxY()),
381:                                new Coordinate(rect.getMaxX(), rect.getMaxY()),
382:                                new Coordinate(rect.getMaxX(), rect.getMinY()),
383:                                new Coordinate(rect.getMinX(), rect.getMinY()) };
384:                        final LinearRing ring = gf.createLinearRing(coord);
385:                        final Polygon modelSpaceROI = new Polygon(ring, null,
386:                                gf);
387:
388:                        // check that we have the same thing here
389:                        assert modelSpaceROI.getEnvelopeInternal().equals(
390:                                new ReferencedEnvelope(rect, cropEnvelope
391:                                        .getCoordinateReferenceSystem()));
392:                        // //
393:                        //
394:                        // Now convert this polygon back into a shape for the source
395:                        // raster space.
396:                        //
397:                        // //
398:                        final List points = new ArrayList(5);
399:                        rasterSpaceROI = FeatureUtilities
400:                                .convertPolygonToPointArray(
401:                                        modelSpaceROI,
402:                                        ProjectiveTransform
403:                                                .create(sourceWorldToGridTransform),
404:                                        points);
405:
406:                        final double cropArea = ((GeneralGridRange) newRange)
407:                                .toRectangle().width
408:                                * ((GeneralGridRange) newRange).toRectangle().height;
409:                        final double roiArea = Math.abs(area((Point2D[]) points
410:                                .toArray(new Point2D[] {})));
411:                        final double roiOpt = parameters.parameter(
412:                                "ROITolerance").doubleValue();
413:                        final String operatioName;
414:                        if (roiOpt * cropArea > roiArea) {
415:                            // executing the mosaic
416:                            final ROIShape roi = new ROIShape(rasterSpaceROI);
417:                            pbj.add(MosaicDescriptor.MOSAIC_TYPE_OVERLAY);
418:                            pbj.add(null);
419:                            pbj.add(new ROI[] { roi });
420:                            pbj.add(null);
421:                            pbj.add(CoverageUtilities
422:                                    .getBackgroundValues(sourceCoverage));
423:                            // nice trick, we use the layout to do the actual crop
424:                            Rectangle2D roiBounds = roi.getBounds2D();
425:                            XRectangle2D.intersect(roiBounds, sourceGridRange
426:                                    .toRectangle(), roiBounds);
427:                            layout.setMinX((int) (roiBounds.getMinX() + 0.5));
428:                            layout.setWidth((int) (roiBounds.getWidth() + 0.5));
429:                            layout.setMinY((int) (roiBounds.getMinY() + 0.5));
430:                            layout
431:                                    .setHeight((int) (roiBounds.getHeight() + 0.5));
432:
433:                            operatioName = "Mosaic";
434:                        } else {
435:                            // executing the crop
436:                            pbj.add(new Float(minX));
437:                            pbj.add(new Float(minY));
438:                            pbj.add(new Float(width));
439:                            pbj.add(new Float(height));
440:                            operatioName = "Crop";
441:                        }
442:
443:                        if (!useProvidedProcessor)
444:                            croppedImage = JAI.create(operatioName, pbj,
445:                                    targetHints);
446:                        else
447:                            croppedImage = processor.createNS(operatioName,
448:                                    pbj, targetHints);
449:                    }
450:                    if (conserveEnvelope.booleanValue())
451:                        return new CroppedCoverage2D(sourceCoverage.getName(),
452:                                croppedImage, new GridGeometry2D(
453:                                        new GeneralGridRange(croppedImage),
454:                                        cropEnvelope), sourceCoverage,
455:                                actionTaken, rasterSpaceROI);
456:                    else
457:                        return new CroppedCoverage2D(
458:                                sourceCoverage.getName(),
459:                                croppedImage,
460:                                new GridGeometry2D(
461:                                        new GeneralGridRange(croppedImage),
462:                                        sourceGridGeometry
463:                                                .getGridToCRS2D(PixelOrientation.CENTER),
464:                                        sourceCoverage
465:                                                .getCoordinateReferenceSystem()),
466:                                sourceCoverage, actionTaken, rasterSpaceROI);
467:
468:                } catch (TransformException e) {
469:                    throw new CannotCropException(Errors
470:                            .format(ErrorKeys.CANT_CROP), e);
471:                } catch (NoninvertibleTransformException e) {
472:                    throw new CannotCropException(Errors
473:                            .format(ErrorKeys.CANT_CROP), e);
474:                }
475:
476:            }
477:
478:            /**
479:             * Function to calculate the area of a polygon, according to the algorithm
480:             * defined at http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
481:             * 
482:             * @param polyPoints
483:             *            array of points in the polygon
484:             * @return area of the polygon defined by pgPoints
485:             */
486:            private static double area(Point2D[] polyPoints) {
487:                int i, j, n = polyPoints.length;
488:                double area = 0;
489:
490:                for (i = 0; i < n; i++) {
491:                    j = (i + 1) % n;
492:                    area += polyPoints[i].getX() * polyPoints[j].getY();
493:                    area -= polyPoints[j].getX() * polyPoints[i].getY();
494:                }
495:                area /= 2.0;
496:                return (area);
497:            }
498:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.