/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ package de.cismet.cids.tools.metaobjectrenderer; import Sirius.navigator.resource.PropertyManager; import com.vividsolutions.jts.geom.Geometry; import org.jdesktop.fuse.ResourceInjector; import org.jdesktop.swingx.image.StackBlurFilter; import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Composite; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Rectangle; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.image.BufferedImage; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JPanel; import de.cismet.cismap.commons.BoundingBox; import de.cismet.cismap.commons.raster.wms.simple.SimpleWMS; import de.cismet.cismap.commons.retrieval.RetrievalEvent; import de.cismet.cismap.commons.retrieval.RetrievalListener; import de.cismet.tools.CismetThreadPool; import de.cismet.tools.gui.FuseLoader; import de.cismet.tools.gui.PainterCoolPanel; /** * DOCUMENT ME! * * @author dmeiers * @version $Revision$, $Date$ */ public class BlurredMapCoolPanel extends PainterCoolPanel implements ComponentListener { //~ Static fields/initializers --------------------------------------------- private static final Dimension MAX_SIZE = new Dimension(2048, 1024); private static final int IMAGE_TYPE = BufferedImage.TYPE_4BYTE_ABGR; // private static final Dimension MAX_SIZE = new Dimension(2048, 1024); public static int offset = 6; public static float blurredMapOpacity = 0.2f; public static float cutOutMapOpacity = 0.6f; public static Color colorMapBorder = Color.black; public static Color gradientColorTop = new Color(120, 120, 120); public static Color gradientColorBottom = new Color(200, 200, 200); //~ Instance fields -------------------------------------------------------- // Lumbermill Logger initialisieren private final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(this.getClass()); private boolean mustBlur; private double geoBuffer; private int lastX; private int lastWidth; private int panelWidth; private Image map; private BufferedImage cacheImage; private BufferedImage blurredMap; private BufferedImage cachedBlurredMap; private BufferedImage gradientImage; private BufferedImage orgMap; private Geometry geometry; private SimpleWMS swms; private JPanel spinner; private JComponent panMap; private JComponent panContent; private Rectangle mapBounds; private ImageIcon icons; private boolean noTitlePanel; //~ Constructors ----------------------------------------------------------- /** * Creates a new BlurredMapCoolPanel object. */ public BlurredMapCoolPanel() { super(); // FUSE initialisieren // Ressourcen hierarchisch rekursiv nach oben einfuegen // NOI18N gradientColorTop = javax.swing.UIManager.getDefaults().getColor("Button.shadow"); // NOI18N gradientColorBottom = javax.swing.UIManager.getDefaults().getColor("Button.background"); // NOI18N mapBounds = null; cacheImage = null; mustBlur = true; geoBuffer = 40d; lastX = 0; lastWidth = 0; panelWidth = 0; map = null; blurredMap = null; orgMap = null; geometry = null; try { final SAXBuilder builder = new SAXBuilder(false); final Document doc = builder.build(getClass().getResource("/coolobjectrenderer/backgroundWMS.xml")); // NOI18N final Element prefs = doc.getRootElement(); swms = new SimpleWMS(prefs); swms.addRetrievalListener(new RetrievalListener() { @Override public void retrievalAborted(final RetrievalEvent retrievalEvent) { } @Override public void retrievalComplete(final RetrievalEvent retrievalEvent) { final Object o = retrievalEvent.getRetrievedObject(); if (o instanceof Image) { map = (Image)o; final BufferedImage erg = new BufferedImage( map.getWidth(null), map.getHeight(null), IMAGE_TYPE); final Graphics2D g = erg.createGraphics(); // g.setColor(Color.green); // g.fillRect(0, 0, map.getWidth(null), map.getHeight(null)); g.drawImage(map, 0, 0, null); g.dispose(); cacheImage = null; lastWidth = getWidth(); lastX = 0; mustBlur = true; if (getSpinner() != null) { getSpinner().setVisible(false); } createBackground(erg); if (log.isDebugEnabled()) { log.debug("MapRetrieval completed"); // NOI18N } } else { if (getSpinner() != null) { getSpinner().setVisible(false); } log.warn("no image"); // NOI18N } } @Override public void retrievalError(final RetrievalEvent retrievalEvent) { if (getSpinner() != null) { getSpinner().setVisible(false); } } @Override public void retrievalProgress(final RetrievalEvent retrievalEvent) { } @Override public void retrievalStarted(final RetrievalEvent retrievalEvent) { if (log.isDebugEnabled()) { log.debug("retrievalStarted"); // NOI18N } } }); } catch (Exception e) { log.error("Error while loading the map info", e); // NOI18N if (getSpinner() != null) { getSpinner().setVisible(false); } } addComponentListener(this); } //~ Methods ---------------------------------------------------------------- @Override public Dimension getMaximumSize() { return MAX_SIZE; } /** * ueberschreibt die Standard-Zeichenmethode eines JPanels. Zeichnet die "coolen" Effekte des CoolPanels. * * @param g DOCUMENT ME! */ @Override protected void paintComponent(final Graphics g) { super.paintComponent(g); final Graphics2D g2d = (Graphics2D)g; if (cacheImage == null) { // Image zum Zeichnen erstellen von dem wird spaeter der Schlagschatten erstellt wird final BufferedImage box = new BufferedImage(getWidth() - offset, getHeight() - offset, IMAGE_TYPE); // Graphics-Objekt der Box erzeugen final Graphics2D bg = box.createGraphics(); /* * Transparenz zeichnen... */ // Standard-Zeichenmodus speichern final Composite orig = bg.getComposite(); final Color origColor = bg.getColor(); bg.setComposite(orig); bg.setColor(origColor); // Karte zeichnen if (getBlurredMap() != null) { bg.setComposite(AlphaComposite.Src.derive(blurredMapOpacity)); bg.drawImage(getBlurredMap(), 0, 0, null); bg.setComposite(orig); // "Fenster zum Hof" ausschneiden und zeichnen, falls panMap gesetzt wurde if (getPanMap() != null) { final Rectangle b = getPanMap().getBounds(); // Karte in Ausschnitt zeichnen if (getMap() != null) { log.info("CoolPanel: draw small map"); // NOI18N bg.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, 0.2f)); bg.setColor(colorMapBorder); if (b.width < getMap().getWidth()) { bg.fillRect( b.x, b.y + panContent.getBounds().y + (2 * offset), b.width - (3 * offset), b.height - (4 * offset)); bg.setComposite(orig); bg.drawRect( b.x, b.y + panContent.getBounds().y + (2 * offset), b.width - (3 * offset), b.height - (4 * offset)); } else { bg.fillRect( b.x, b.y + panContent.getBounds().y + (2 * offset), getMap().getWidth(), b.height - (4 * offset)); bg.setComposite(orig); bg.drawRect( b.x, b.y + panContent.getBounds().y + (2 * offset), getMap().getWidth(), b.height - (4 * offset)); } // Fensterausschnitt zeichnen bg.setComposite(AlphaComposite.DstOver.derive(cutOutMapOpacity)); final BufferedImage subMap = getMap().getSubimage( 0, 0, getMap().getWidth(), getMap().getHeight() - (4 * offset)); bg.drawImage(subMap, b.x, b.y + panContent.getBounds().y + (2 * offset), null); } } } // Ende Karte zeichnen // rander sauber zeichen // Fertige Box und ihren Schatten zeichnen. cacheImage = new BufferedImage(box.getWidth(), box.getHeight(), IMAGE_TYPE); final Graphics2D cg = cacheImage.createGraphics(); final Composite cgOrigComp = cg.getComposite(); final Color cgOrigColor = cg.getColor(); cg.setColor(Color.red); cg.fillRoundRect(offset, 0, cacheImage.getWidth(), cacheImage.getHeight(), 30, 30); cg.setComposite(AlphaComposite.SrcIn); cg.setColor(cgOrigColor); cg.drawImage(box, offset, 0, null); cg.setComposite(orig); bg.dispose(); cg.dispose(); box.flush(); } // Entgueltiges Bild in Panel zeichnen g2d.drawImage(cacheImage, 0, 0, null); } /** * Liefert den weichgezeichneten Kartenausschnitt zurueck. * * @return der bereits weichgezeichnete Kartenausschnitt */ private BufferedImage getBlurredMap() { return blurredMap; } /** * Liefert den Kartenausschnitt ohne Weichzeichner zurueck. * * @return der "originale" Kartenausschnitt */ private BufferedImage getMap() { return orgMap; } /** * Setzt den relevanten Kartenausschnitt. Hierbei wird der Ausschnitt automatisch weichgezeichnet. Um diese Karte im * CoolPanel zu verwenden sollte er mit der {@link getBlurredMap()}-Methode aufgerufen werden. * * @param newMap der geaenderte zu blurrende Kartenauschnitt */ private void createBackground(final BufferedImage newMap) { if (newMap != null) { orgMap = newMap; // Ausfuehrung des Blurrens im Thread final Thread t = new Thread(new Runnable() { @Override public void run() { if (mustBlur) { final StackBlurFilter blur = new StackBlurFilter(6); cachedBlurredMap = blur.filter(orgMap, null); blurredMap = new BufferedImage(getWidth(), getHeight(), IMAGE_TYPE); final Graphics2D b = blurredMap.createGraphics(); b.drawImage(cachedBlurredMap, 0, 0, null); b.dispose(); } else { blurredMap.flush(); final Graphics2D b = blurredMap.createGraphics(); b.drawImage(cachedBlurredMap, lastX, 0, null); b.dispose(); } if (getPanMap() != null) { final Rectangle bounds = getPanMap().getBounds(); if ((bounds.width - (3 * offset)) > getWidth()) { orgMap = orgMap.getSubimage( bounds.x, bounds.y + (2 * offset), getWidth(), bounds.height); } else { orgMap = orgMap.getSubimage( bounds.x, bounds.y + (2 * offset), bounds.width - (3 * offset), bounds.height); } } // CoolPanel neu zeichnen, sobald die geblurrte Karte fertig erstellt wurde EventQueue.invokeLater(new Runnable() { @Override public void run() { cacheImage = null; repaint(); } }); } }, "BlurredMapCoolPanel createBackground()"); CismetThreadPool.execute(t); } } /** * Interne Methode die den WMS-Server anstoesst eine neue Karte zu liefern. */ private void mapIt() { if (log.isDebugEnabled()) { log.debug("MAPIT"); // NOI18N } try { if ((getSpinner() != null) && !getSpinner().isVisible()) { getSpinner().setVisible(true); } if (geometry != null) { mapBounds = getBounds(); // Neue BoundingBox 40 Einheiten um die Geometrie herum erzeugen BoundingBox bb = new BoundingBox(geometry.buffer(geoBuffer)); // Panelgroessen speichern this.panelWidth = getWidth(); if (getPanMap() != null) { final double panWith = new Integer(getWidth() - (3 * offset)).doubleValue(); final double panHeight = new Integer(getHeight() - (4 * offset)).doubleValue(); final double cutOutWidth = new Integer(getPanMap().getWidth() - offset).doubleValue(); final double cutOutHeight = new Integer(getPanMap().getHeight() - offset).doubleValue(); // Mittelpunkt der BoundingBox bestimmen final double midX = bb.getX1() + ((bb.getX2() - bb.getX1()) / 2); final double midY = bb.getY1() + ((bb.getY2() - bb.getY1()) / 2); // Groesse des Kartenausschnitts in WMS-Einheiten speichern double worldWidth = bb.getWidth(); double worldHeight = bb.getHeight(); // Verhaeltnis Breite/Hoehe berechnen final double widthToHeightRatio = cutOutWidth / cutOutHeight; // Testen, wie die RealWorld-Groessen angepasst werden muessen if ((widthToHeightRatio / (worldWidth / worldHeight)) > 1) { // Breite der Hoehe anpassen worldWidth = worldHeight * widthToHeightRatio; } else { // Hoehe der Breite anpassen worldHeight = worldWidth * widthToHeightRatio; } // Pixel-WMS-Einheit-Verhaeltnis bezeichnen final double widthValuePerPixel = worldWidth / cutOutWidth; final double heightValuePerPixel = worldHeight / cutOutHeight; final double foo = worldWidth / widthValuePerPixel; // Versatz berechnen final Rectangle b = getPanMap().getBounds(); final double offTop = b.getY() * heightValuePerPixel; final double offBottom = (panHeight - (b.getY() + b.height)) * heightValuePerPixel; final double offLeft = b.getX() * widthValuePerPixel; final double offRight = (panWith - (b.getX() + b.width - (3 * offset))) * widthValuePerPixel; // BoundingBox mit neuer Groesse erstellen bb = new BoundingBox((midX - ((worldWidth / 2) + offLeft)), (midY - ((worldHeight / 2) + offTop) - 95), (midX + (worldWidth / 2) + offRight), (midY + (worldHeight / 2) + offBottom) - 95); } else { final double midX = bb.getX1() + ((bb.getX2() - bb.getX1()) / 2); final double midY = bb.getY1() + ((bb.getY2() - bb.getY1()) / 2); double realWorldWidth = bb.getWidth(); double realWorldHeight = bb.getHeight(); final double widthToHeightRatio = getWidth() / getHeight(); if ((widthToHeightRatio / (realWorldWidth / realWorldHeight)) > 1) { // height is bestimmer ;-) realWorldWidth = realWorldHeight * widthToHeightRatio; } else { realWorldHeight = realWorldWidth * widthToHeightRatio; } bb = new BoundingBox(midX - (realWorldWidth / 2), midY - (realWorldHeight / 2), midX + (realWorldWidth / 2), midY + (realWorldHeight / 2)); } // Karte von WMS-Server holen swms.setBoundingBox(bb); swms.setSize(getHeight(), getWidth()); swms.retrieve(true); } else { if (log.isDebugEnabled()) { log.debug("No geometry object available."); // NOI18N } if (getSpinner() != null) { getSpinner().setVisible(false); } repaint(); } } catch (Exception e) { log.warn("Error while displaying the map.", e); // NOI18N if (getSpinner() != null) { getSpinner().setVisible(false); } } } /** * Setzt das Geometry-Objekt des momentan im Konfigurator angewaehlten Objekts. Wird benoetigt, um einen * Kartenhintergrund zu zeichnen. * * @param geometry Geometry-Objekt */ public void setGeometry(final Geometry geometry) { this.geometry = geometry; } @Override public void componentResized(final ComponentEvent e) { cacheImage = null; if ((getMap() == null) || (mapBounds.height < getHeight()) || (mapBounds.width < getWidth())) { map = null; blurredMap = null; orgMap = null; repaint(); mapIt(); } else { // Test, ob Groesse zum ersten Mal geaendert wird if (lastWidth == 0) { lastWidth = panelWidth; } final int width = getWidth(); // Test, ob vergroessert oder verkleinert wird if (width > lastWidth) { lastX += (width - lastWidth) / 2; } else { lastX -= (lastWidth - width) / 2; } // letzte Panelbreite speichern fuer naechsten Aufruf lastWidth = width; // neues Hintergrundbild erstellen und zeichnen final BufferedImage erg = new BufferedImage(getWidth(), getHeight(), IMAGE_TYPE); final Graphics2D g = erg.createGraphics(); g.drawImage(map, lastX, 0, null); g.dispose(); // ChachedBlurImage verwenden mustBlur = false; createBackground(erg); } } @Override public void componentMoved(final ComponentEvent e) { } @Override public void componentShown(final ComponentEvent e) { mapIt(); } @Override public void componentHidden(final ComponentEvent e) { } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public JComponent getPanMap() { return panMap; } /** * DOCUMENT ME! * * @param panMap DOCUMENT ME! */ public void setPanMap(final JComponent panMap) { this.panMap = panMap; } /** * DOCUMENT ME! * * @param panContent DOCUMENT ME! */ public final void setPanContent(final JComponent panContent) { this.panContent = panContent; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public JPanel getSpinner() { return spinner; } /** * DOCUMENT ME! * * @param spinner DOCUMENT ME! */ public void setSpinner(final JPanel spinner) { this.spinner = spinner; } }