
org.geojsf.component.Map Maven / Gradle / Ivy
The newest version!
package org.geojsf.component;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
import javax.faces.application.ResourceDependencies;
import javax.faces.application.ResourceDependency;
import javax.faces.component.FacesComponent;
import javax.faces.component.UIComponent;
import javax.faces.component.UINamingContainer;
import javax.faces.component.behavior.ClientBehavior;
import javax.faces.component.behavior.ClientBehaviorHolder;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.event.ListenerFor;
import javax.faces.event.PostAddToViewEvent;
import org.geojsf.component.entities.OlLayer;
import org.geojsf.component.entities.OlService;
import org.geojsf.event.MapAjaxEvent;
import org.geojsf.interfaces.model.core.GeoJsfCategory;
import org.geojsf.interfaces.model.core.GeoJsfLayer;
import org.geojsf.interfaces.model.core.GeoJsfMap;
import org.geojsf.interfaces.model.core.GeoJsfService;
import org.geojsf.interfaces.model.core.GeoJsfView;
import org.geojsf.interfaces.model.meta.GeoJsfDataSource;
import org.geojsf.interfaces.model.meta.GeoJsfViewPort;
import org.geojsf.interfaces.model.sld.GeoJsfSld;
import org.geojsf.interfaces.model.sld.GeoJsfSldRule;
import org.geojsf.interfaces.model.sld.GeoJsfSldTemplate;
import org.geojsf.model.xml.geojsf.Scales;
import org.geojsf.model.xml.specs.gml.Coordinates;
import org.geojsf.util.GeoJsfJsLoader;
import org.geojsf.util.component.GeoJsfScalesUtil;
import org.primefaces.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import net.sf.ahtutils.interfaces.model.graphic.UtilsGraphic;
import net.sf.ahtutils.interfaces.model.status.UtilsDescription;
import net.sf.ahtutils.interfaces.model.status.UtilsLang;
import net.sf.ahtutils.interfaces.model.status.UtilsStatus;
@ResourceDependencies({
@ResourceDependency(library = "javax.faces", name = "jsf.js", target = "head"),
@ResourceDependency(library = "geoJsfCss" , name = "ol.css", target = "head"),
@ResourceDependency(library = "geojsf", name = "geojsf.css", target = "head")})
@FacesComponent(value="org.geojsf.component.Map")
@ListenerFor(systemEventClass=PostAddToViewEvent.class)
public class Map ,GT extends UtilsStatus,GS extends UtilsStatus,
CATEGORY extends GeoJsfCategory,
SERVICE extends GeoJsfService,
LAYER extends GeoJsfLayer,
MAP extends GeoJsfMap,
VIEW extends GeoJsfView,
VP extends GeoJsfViewPort,
DS extends GeoJsfDataSource,
SLDTEMPLATE extends GeoJsfSldTemplate,
SLDTYPE extends UtilsStatus,
SLD extends GeoJsfSld,
RULE extends GeoJsfSldRule>
extends UINamingContainer implements ClientBehaviorHolder
{
final static Logger logger = LoggerFactory.getLogger(Map.class);
Gson gson = new Gson();
Hashtable serviceList;
// This is taken directly from the value attribute or is constructed from given LAYER components
private MAP dmMap;
private List temporalLayerNames;
private String baseMap;
public String getBaseMap() {return baseMap;}
public void setBaseMap(String baseMap) {this.baseMap = baseMap;}
// Internal fields
private Coordinates coords = new Coordinates();
private Boolean initStage = true;
private Boolean refreshLayersOnUpdate = true;
private Boolean changedState = false;
private Long layerThatChanged = (long) 0;
private Scales scales = null;
private String timeInfo = null;
private ArrayList orderedLayers = new ArrayList();
//Define attributes of the component
private Integer width = null;
private Integer height = 400;
//These are related to the switching of layers
Hashtable layerNames;
@Override
public void processEvent(ComponentSystemEvent event) throws AbortProcessingException
{
if(event instanceof PostAddToViewEvent)
{
GeoJsfJsLoader.pushJsToHead(this.getFacesContext(),"ol-debug.js");
GeoJsfJsLoader.pushJsToHead(this.getFacesContext(),"GeoJSF3.js");
}
else
{
logger.debug("received ComponentSystemEvent: " +event.getClass().getName());
}
super.processEvent(event);
}
// --------------------------
// JSF Encode Phase methods
// --------------------------
@Override
public void encodeBegin(FacesContext ctx) throws IOException
{
/* if (null == dmMap)
{
logger.error("There is no data);
this.setRendered(false);
for (UIComponent child : this.getChildren())
{
child.setRendered(false);
}
}*/
if (this.isRendered())
{
logger.debug("entering encodebegin");
try
{
if (!containsLayer())
{
dmMap = (MAP)getAttributes().get("value");
}
else
{
MapUtil mapUtil = new MapUtil(ctx);
dmMap = (MAP) mapUtil.buildViewsFromLayers(this);
}
layerNames = new Hashtable();
}
catch (Exception e)
{
logger.warn("Problem occured when processing layers: " +e.getMessage());
e.printStackTrace();
this.setRendered(false);
}
ResponseWriter writer = ctx.getResponseWriter();
JsfRenderUtil renderer = new JsfRenderUtil(writer, this);
renderer.renderLinebreaks(2);
// Render the DIV container that will be used by OpenLayers to inject the OpenLayers map
renderer.renderDiv(this.getClientId(), MapUtil.buildStyle(height, width));
renderer.renderLinebreaks(1);
// Check if the list of VIEWs is empty
if (null == dmMap || dmMap.getViews().isEmpty())
{
writer.startElement("center", this);
writer.writeText("no layers given.", null);
writer.endElement("center");
logger.warn("Map can not be rendered without layers!");
}
else
{
if(true)
{
// First - build a list of OpenLayers Services from the list of VIEWs. These are used to render the map layers initially
serviceList = new Hashtable();
// So collect all information from VIEWs
Hashtable serviceForUrl = new Hashtable();
for (VIEW view : dmMap.getViews())
{
LAYER viewLayer = view.getLayer();
SERVICE viewService = view.getLayer().getService();
OlLayer layer = new OlLayer();
layer.setId(viewLayer.getId());
layer.setName(viewLayer.getCode());
layer.setVisible(view.isVisible());
if (!serviceForUrl.containsKey(viewService.getWms()))
{
OlService service = new OlService();
service.setUrl(viewService.getWms());
service.setId(viewService.getId());
service.setLayerVisibility(new LinkedHashMap());
service.getLayerVisibility().put(viewLayer.getId(), view.isVisible());
layerNames.put(viewLayer.getId(), viewLayer.getCode());
serviceForUrl.put(viewService.getWms(), service);
orderedLayers.add(service.getId());
logger.debug("Added layer " +viewLayer.getCode() +" to be " +view.isVisible());
}
else
{
OlService service = serviceForUrl.get(viewService.getWms());
layerNames.put(viewLayer.getId(), viewLayer.getCode());
service.getLayerVisibility().put(viewLayer.getId(), view.isVisible());
// orderedLayers.add(service.getId());
logger.debug("Added layer " +viewLayer.getCode() +" to be " +view.isVisible() +" to existing Service.");
}
}
// And add all constructed OpenLayers Services to the internal serviceList
for (String url : serviceForUrl.keySet())
{
OlService service = serviceForUrl.get(url);
serviceList.put(service.getId(), service);
}
logger.debug("Transfered " +serviceList.size() +" services from " +serviceForUrl.size() +" and the size of orderedLayers is " +orderedLayers.size());
//First, render the JavaScript code to initialize the map
renderer.renderMapInitialization(this.getFacesContext());
//Next, render the layers
renderLayers(writer, renderer, false);
//Set flag that map initiation is completed and not to be repeated
initStage = false;
logger.info("Map initialization completed for " +this.getClientId() +" (ID: " +this.getId() +") holding " +serviceList.size() +" layers.");
writer.endElement("script");
}
}
}
}
public void renderLayers(ResponseWriter writer, JsfRenderUtil renderer, Boolean addScriptTag) throws IOException
{
if(addScriptTag){writer.startElement("script", this);}
//First, render the base layer (adding overlays first results in error)
Long baseLayerId = orderedLayers.get(orderedLayers.size()-1);
if (orderedLayers.size()==1)
{
encodeOlService(serviceList.get(baseLayerId), true, true, writer, renderer);
}
else
{
encodeOlService(serviceList.get(baseLayerId), true, false, writer, renderer);
}
renderer.renderTextWithLB("GeoJSF.registerEventHandlers();");
//Finally, render the overlay layers
//for (int i=0;i localTemporalLayer = MapUtil.hasTemporalLayer(service);
if (localTemporalLayer.size()>0 && null!=timeInfo)
{
logger.info("Found time layers in Service " +service.getId() +": " +localTemporalLayer.toString());
// temporalLayerNames.addAll(localTemporalLayer);
temporalLayerNames.add(service.getId() +"");
renderer.renderTextWithLB("params.time = '"+timeInfo +"';");
}
*/
Hashtable parameters = MapUtil.searchSqlViewParameters(this);
if (parameters.size()>0)
{
StringBuffer sb = new StringBuffer();
sb.append("params.viewparams = '");
for (String key : parameters.keySet())
{
sb.append(key +":" +parameters.get(key) +";");
}
sb.append("';");
// renderer.renderTextWithLB(sb.toString());
}
renderer.renderTextWithLB("var options = {};");
// Process scales definition
Scales scales = MapUtil.searchScale(this);
if (scales!=null)
{
// Define all allowed levels
String scaleDefinitions = "options.scales = [";
GeoJsfScalesUtil scalesUtil = new GeoJsfScalesUtil(scales);
scaleDefinitions += scalesUtil.getScaleList();
scaleDefinitions += "];" +System.getProperty("line.separator");
writer.writeText(scaleDefinitions, null);
// Define minimum and maximum values
writer.writeText("options.maxScale = "+scalesUtil.getMax() +";" +System.getProperty("line.separator"), null);
writer.writeText("options.minScale = "+scalesUtil.getMin() +";" +System.getProperty("line.separator"), null);
// Set the unit
writer.writeText("options.units = '" +scales.getUnit() +"';" +System.getProperty("line.separator"), null);
}
renderer.renderTextWithLB("GeoJSF.addLayer(name, url, params, options);");
renderer.renderLinebreaks(1);
}
// --------------------------
// JSF Decode Phase method
// --------------------------
public void decode(FacesContext context)
{
logger.debug("Current Phase: " +context.getCurrentPhaseId().toString());
if (this.isRendered())
{
java.util.Map params = context.getExternalContext().getRequestParameterMap();
String behaviorEvent = params.get("javax.faces.behavior.event");
logger.debug("Handling event of type: " +behaviorEvent +" in decode phase.");
// Create a new GeoJsfMap from the given (maybe manipulated) map object
try
{
dmMap = (MAP)getAttributes().get("value");
}
catch (Exception e) {e.printStackTrace();}
if (null == serviceList)
{
logger.error("NO SERVICES?");
return;
}
logger.debug("Service List has " +serviceList.size() +" entries in decode after restore.");
// This will be compared to the values stored in the session before (activate this to check)
// logger.info("Services: " +new LayerSwitchHelper(services, layerNames).toString());
// For better reading of the following, some Booleans are defined here
Boolean isLayerSwitchEvent = false;
Boolean isUpdateMapEvent = false;
Boolean isUpdateModelEvent = false;
Boolean isUpdateTimeEvent = false;
Boolean isUpdateParamsEvent= false;
Boolean isMapClickEvent = false;
Boolean isMapMoveEvent = false;
if (null!=behaviorEvent)
{
isLayerSwitchEvent = behaviorEvent.equals("layerSwitch");
isUpdateMapEvent = behaviorEvent.equals("updateMap");
isUpdateModelEvent = behaviorEvent.equals("updateModel");
isUpdateTimeEvent = behaviorEvent.equals("updateTime");
isUpdateParamsEvent= behaviorEvent.equals("updateParams");
isMapClickEvent = behaviorEvent.equals("mapClick");
isMapMoveEvent = behaviorEvent.equals("mapMove");
}
//if (null!=services && ((null != behaviorEvent && !isLayerSwitchEvent) || null==behaviorEvent || isUpdateMapEvent))
if (null!=serviceList)
{
// Load the (maybe updated) SqlViewParameters
logger.debug("Searching for SQLVP in Decode");
Hashtable parameters = MapUtil.searchSqlViewParameters(this);
StringBuffer sb = new StringBuffer();
if (parameters.size()>0)
{
sb.append("'");
for (String key : parameters.keySet())
{
sb.append(key +":" +parameters.get(key) +";");
}
sb.append("';");
}
// Iterate through all Views (that hold the information if a Layer is visible)
for (VIEW view : dmMap.getViews())
{
Long serviceId = view.getLayer().getService().getId();
Long layerId = view.getLayer().getId();
// Check the visibility value before and currently
Boolean before = (Boolean) serviceList.get(serviceId).isLayerVisible(layerId);
Boolean now = view.isVisible();
logger.trace("Checking " +dmMap.getViews().size() +"of dmMap for changes.");
logger.trace("Checking layer " +view.getLayer().getCode());
if (before.equals(now))
{
// If there is no change, do nothing
logger.debug("Layer " +view.getLayer().getCode() +" did not change it's visibility. Still " +view.isVisible());
}
else
{
// In case the stored state and the current state of a view (= layer visibility) is different, prepare updating the map
changedState = now;
layerThatChanged = view.getLayer().getId();
logger.debug("Layer " +layerThatChanged +" changed to " +changedState);
}
}
}
// Handling of mapClick event fired by JavaScript API
if (null!= behaviorEvent && isMapClickEvent)
{
java.util.Map> behaviors = getClientBehaviors();
if (behaviors.isEmpty())
{
logger.debug("no behaviors.exiting.");
return;
}
List behaviorsForEvent = behaviors.get(behaviorEvent);
if (behaviors.size() > 0)
{
String behaviorSource = params.get("javax.faces.source");
String clientId = getClientId(context);
if (behaviorSource != null && null!= behaviorsForEvent && behaviorSource.equals(clientId))
{
for (ClientBehavior behavior: behaviorsForEvent)
{
logger.trace("Found " +behavior.getClass().toString());
MapAjaxEvent ajaxEvent = new MapAjaxEvent(this, behavior);
logger.trace("Setting CLiCK ");
ajaxEvent.setClickCoordinates(params);
ajaxEvent.setViewport(params);
behavior.broadcast(ajaxEvent);
}
}
}
}
// Handling of mapMove event fired by JavaScript API
if (null!= behaviorEvent && isMapMoveEvent)
{
java.util.Map> behaviors = getClientBehaviors();
if (behaviors.isEmpty())
{
logger.debug("no behaviors.exiting.");
return;
}
List behaviorsForEvent = behaviors.get(behaviorEvent);
if (null != behaviorsForEvent)
{
String behaviorSource = params.get("javax.faces.source");
String clientId = getClientId(context);
if (behaviorSource != null && behaviorSource.equals(clientId))
{
for (ClientBehavior behavior: behaviorsForEvent)
{
logger.trace("Found " +behavior.getClass().toString());
MapAjaxEvent ajaxEvent = new MapAjaxEvent(this, behavior);
ajaxEvent.setViewport(params);
behavior.broadcast(ajaxEvent);
}
}
}
}
// Handling of mapClick event fired by JavaScript API
if (null!= behaviorEvent && isUpdateTimeEvent)
{
logger.info("Received updateTime event from JavaScript API.");
// Read current time from request
// Time is given in ms format as String
String timeString = params.get("org.geojsf.update.time");
Long time = Long.parseLong(timeString);
MapUtil.updateTime(this, time);
}
if (null!=serviceList && isUpdateMapEvent)
{
try{
logger.trace("State Problem?");
logger.debug("Looking for the changed layer: " +layerThatChanged +" now in state " +changedState);
VIEW view = null;
for (VIEW viewIterated : dmMap.getViews())
{
if (viewIterated.getLayer().getId() == layerThatChanged)
{
view = viewIterated;
logger.trace("Found view to be changed: " +view.getLayer().getCode());
}
}
// If there is a new value, generate a command to toggle the assigned service in OpenLayers using hide/show/merge
logger.info("Changing visibility of layer (" +view.getLayer().getId() +") " +view.getLayer().getCode() +" of Service " +view.getLayer().getService().getId() +" to " +changedState);
String toggleCommand = toggleLayer(view.getLayer().getService().getId(), view.getLayer().getId(), view.isVisible());
// Now send the command (JSON-String representation of the Command object) to the client using PrimeFaces CallbackParam methodology
// This can be used in the oncomplete AJAX-callback JavaScript function like performLayerSwitch(xhr, status, args)
logger.debug("Sending layer switch command to JavaScript client logic: " +toggleCommand +" to switch layer " +view.getLayer().getId() +" of service " +view.getLayer().getService().getId() +" to " +view.isVisible());
RequestContext.getCurrentInstance().addCallbackParam("toggleLayer", toggleCommand);
} catch (Exception e)
{
logger.error(e.getMessage());
}
}
}
}
public String toggleLayer(Long serviceId, Long layerId, Boolean active)
{
Order order = new Order();
String command = null;
OlService service = serviceList.get(serviceId);
// The following code is building the list of layers that are to be shown after the command is executed
// First, set the requested layer to the requested status
service.setVisibility(layerId, active);
// Then process the complete list of layers assigned to the requested layers service
ArrayList activeLayers = new ArrayList();
for (Long layerOfService : service.getLayerVisibility().keySet())
{
Boolean visible = service.isLayerVisible(layerOfService);
if (visible) {activeLayers.add(layerNames.get(layerOfService));}
}
// Use MERGE command as standard
command = "merge";
// Now let's see if there is another option that is more appropriate
//If the only layer active is the one that should be hidden, hide the whole service
if (activeLayers.size()==0 && !active && service.getLayerVisibility().size()==1)
{command = "hide";}
//If the layer should be shown and the service is hidden right now and there is only one layer in the service, show it back
if (active && activeLayers.size()==1 && service.getLayerVisibility().size()==1)
{command = "show";}
//This is obsolete since it is covered indirectly by the code above
//If there are other active layers, merge the newly constructed service
/* if (activeLayers.size()>1 && !active && service.getLayer().size()>1)
{command = "merge";}
//If the layer should be shown and the service is hidden right now and there is only one layer in the service, show it back
if (active && activeLayers.isEmpty() && service.getLayer().size()>1)
{command = "merge";}
*/
// To have the correct layer allignement for OpenLayers, reverse the list order
Collections.reverse(activeLayers);
// Create the layer switch command for JavaScript GeoJSF logic
order.setCommand(command);
order.setServiceId(serviceId);
order.setLayer(activeLayers);
return gson.toJson(order);
}
// -------------------------------------------
// JSF Methods for State Saving and Event Name
// -------------------------------------------
@SuppressWarnings("unchecked")
@Override
public void restoreState(FacesContext context, Object state)
{
if (this.isRendered())
{
Object[] storedState = (Object[]) state;
logger.debug("Restoring state.");
try {
initStage = (Boolean) storedState[0];
serviceList = (Hashtable) storedState[1];
layerNames = (Hashtable) storedState[3];
dmMap = (MAP) storedState[4];
layerThatChanged = (Long) storedState[5];
changedState = (Boolean) storedState[6];
}
catch (Exception e)
{
logger.error("Exception when restoring: " +e.getMessage());
}
}
}
@Override
public Object saveState(FacesContext context)
{
Object[] rtrn = new Object[7];
if (this.isRendered())
{
rtrn[0] = initStage;
rtrn[1] = serviceList;
rtrn[3] = layerNames;
rtrn[4] = dmMap;
rtrn[5] = layerThatChanged;
rtrn[6] = changedState;
}
return rtrn;
}
@Override
public Collection getEventNames()
{
ArrayList events = new ArrayList();
events.add("mapClick");
events.add("mapMove");
events.add("updateModel");
events.add("updateParams");
return events;
}
@Override
public String getDefaultEventName() {return "mapClick";}
public String buildLayerString(OlService service)
{
StringBuffer sb = new StringBuffer();
List list = new ArrayList();
list.addAll(service.getLayerVisibility().keySet());
Collections.reverse(list);
for (Long i : list)
{
if (service.getLayerVisibility().get(i))
{
sb.append(layerNames.get(i) +",");
}
}
if (sb.length()>0)
{
sb.deleteCharAt(sb.length()-1);
}
return sb.toString();
}
public Boolean containsLayer()
{
Boolean layer = false;
for (UIComponent child : this.getChildren())
{
if (child instanceof Layer)
{
layer = true;
}
}
return layer;
}
// --------------------------
// Standard Getter and Setter
// --------------------------
public Integer getWidth(){return width;}
public void setWidth(Integer width) {this.width = width;}
public Integer getHeight() {return height;}
public void setHeight(Integer height) {this.height = height;}
public Coordinates getCoords() {return coords;}
public void setCoords(Coordinates coords) {this.coords = coords;}
public Hashtable getServiceList() {return serviceList;}
public void setServiceList(Hashtable serviceList) {this.serviceList = serviceList;}
public List getTemporalLayerNames() {return temporalLayerNames;}
public void setTemporalLayerNames(List temporalLayerNames) {this.temporalLayerNames = temporalLayerNames;}
public Scales getScales() {return scales;}
public void setScales(Scales scales) {this.scales = scales;}
public String getTimeInfo() {return timeInfo;}
public void setTimeInfo(String timeInfo) {this.timeInfo = timeInfo;}
public Boolean getInitStage() {return initStage;}
public void setInitStage(Boolean initStage) {this.initStage = initStage;}
public Boolean getRefreshLayersOnUpdate() {return refreshLayersOnUpdate;}
public void setRefreshLayersOnUpdate(Boolean refreshLayersOnUpdate) {this.refreshLayersOnUpdate = refreshLayersOnUpdate;}
public MAP getDmMap() {return dmMap;}
public void setDmMap(MAP dmMap) {this.dmMap = dmMap;}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy