我正在使用JSF2和Primefaces作为WebFrameworks开发Java Web应用程序。我的应用程序旨在管理相机。有关摄像机的所有数据都存储在PostgreSQL数据库中。我有一个JSF视图,其中我使用Leaflet的Javascript API显示动态地图。 您可以通过以下链接查看此类地图的示例:Leaflet example。
总而言之,我的地图旨在展示相机的标记。使用摄像机的属性生成标记的位置。
我创建了一个复合组件及其Backing Bean来处理地图。这是视图的源代码:
<h:body>
<!-- INTERFACE -->
<composite:interface componentType="mapComponent">
</composite:interface>
<!-- IMPLEMENTATION -->
<composite:implementation>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="#{resource['css:leaflet.css']}" />
<!--[if lte IE 8]><link rel="stylesheet" href="#{resource['css:leaflet.ie.css']}" /><![endif]-->
<script
src="#{resource['js:leaflet/Leaflet-v0.4.4-0/dist/leaflet-src.js']}"></script>
<script
src="#{resource['js:leaflet/Leaflet-v0.4.4-0/src/leafclusterer.js']}"></script>
<div id="#{cc.mapId}" style="width: 520px; height: 580px" />
<script type="text/javascript">
//<![CDATA[
var clusterPopup;
function onClustererClick(cluster, coordinates) {
var markers = cluster.getMarkers;
var popupContent = "<p>";
for (var i = 0; i < cluster.getCluster().getMarkers().length; i++) {
var currentMarker = cluster.getCluster().getMarkers()[i];
popupContent += currentMarker.marker._popup._content
popupContent += "<br/>";
}
popupContent += "</p>";
clusterPopup = new L.Popup();
clusterPopup.setLatLng(coordinates);
clusterPopup.setContent(popupContent);
map.openPopup(clusterPopup);
};
var cameraIcon = L.icon({
iconUrl : "#{resource['img:leaflet/camera.png']}",
iconSize : [ 48, 48 ],
shadowSize : [ 48, 48 ],
iconAnchor : [ 30, 30 ],
popupAnchor : [ 0, -20 ]
});
var marker;
function onMapClick(e) {
marker = new L.marker(e.latlng, {
draggable : true,
icon : cameraIcon
});
map.addLayer(marker);
marker.bindPopup("<b>Hello world!</b><br />I am a popup.");
map.off('click');
};
var osmUrl = '#{cc.osmUrl}';
var osmAttrib = '#{cc.osmAttrib}';
var basic = new L.tileLayer(osmUrl, {
minZoom : #{cc.minZoom},
maxZoom : #{cc.maxZoom},
attribution : osmAttrib
});
var map = new L.Map('#{cc.mapId}', {
center : new L.LatLng(#{cc.mapX}, #{cc.mapY}),
zoom : #{cc.mapDefaultZoom},
layers : [ basic ]
});
var baseMaps = {
"#{cc.mapBasicLayerName}" : basic
};
if(#{cc.mapEnableOnClick}) {
map.on('click', onMapClick);
}
var clusterer = new LeafClusterer(map);
//]]>
</script>
<composite:insertChildren />
</composite:implementation>
</h:body>
</html>
这是我的支持bean的代码:
@FacesComponent(value = "mapComponent")
public class MapComponent extends UINamingContainer {
private static final String MAP_ID_PROP = "map_id";
private static final String OSM_URL_PROP = "osm_url";
private static final String OSM_ATTRIB_PROP = "osm_attrib";
private static final String MIN_ZOOM_PROP = "min_zoom";
private static final String MAX_ZOOM_PROP = "max_zoom";
private static final String MAP_X_PROP = "map_x";
private static final String MAP_Y_PROP = "map_y";
private static final String MAP_DEFAULT_ZOOM_PROP = "map_default_zoom";
private static final String MAP_BASIC_LAYER_NAME_PROP = "map_basic_layer_name";
private static final String MAP_ENABLE_ON_CLICK_PROP = "map_enable_on_click";
private String mapId;
private String osmUrl;
private String osmAttrib;
private Integer minZoom;
private Integer maxZoom;
private Double mapX;
private Double mapY;
private Integer mapDefaultZoom;
private String mapBasicLayerName;
private Boolean mapEnableOnClick;
/**
* Instanties un nouveau map component.
*/
public MapComponent() {
super();
mapId = (String) AppUtil.getProperty(MAP_ID_PROP);
osmUrl = (String) AppUtil.getProperty(OSM_URL_PROP);
osmAttrib = (String) AppUtil.getProperty(OSM_ATTRIB_PROP);
minZoom = Integer.parseInt((String) AppUtil.getProperty(MIN_ZOOM_PROP));
maxZoom = Integer.parseInt((String) AppUtil.getProperty(MAX_ZOOM_PROP));
mapX = Double.parseDouble((String) AppUtil.getProperty(MAP_X_PROP));
mapY = Double.parseDouble((String) AppUtil.getProperty(MAP_Y_PROP));
mapDefaultZoom = Integer.parseInt((String) AppUtil.getProperty(MAP_DEFAULT_ZOOM_PROP));
mapBasicLayerName = (String) AppUtil.getProperty(MAP_BASIC_LAYER_NAME_PROP);
setMapEnableOnClick(Boolean.parseBoolean((String) AppUtil.getProperty(MAP_ENABLE_ON_CLICK_PROP)));
}
/**
* Getter : retourne le map id.
*
* @return le map id
*/
public String getMapId() {
return mapId;
}
/**
* Getter : retourne le osm url.
*
* @return le osm url
*/
public String getOsmUrl() {
return osmUrl;
}
/**
* Getter : retourne le osm attrib.
*
* @return le osm attrib
*/
public String getOsmAttrib() {
return osmAttrib;
}
/**
* Getter : retourne le min zoom.
*
* @return le min zoom
*/
public Integer getMinZoom() {
return minZoom;
}
/**
* Getter : retourne le max zoom.
*
* @return le max zoom
*/
public Integer getMaxZoom() {
return maxZoom;
}
/**
* Getter : retourne le map x.
*
* @return le map x
*/
public Double getMapX() {
return mapX;
}
/**
* Getter : retourne le map y.
*
* @return le map y
*/
public Double getMapY() {
return mapY;
}
/**
* Getter : retourne le map default zoom.
*
* @return le map default zoom
*/
public Integer getMapDefaultZoom() {
return mapDefaultZoom;
}
/**
* Getter : retourne le map basic layer name.
*
* @return le map basic layer name
*/
public String getMapBasicLayerName() {
return mapBasicLayerName;
}
/**
* Setter : affecte le map id.
*
* @param mapId le map id
*/
public void setMapId(String mapId) {
this.mapId = mapId;
}
/**
* Setter : affecte le osm url.
*
* @param osmUrl le osm url
*/
public void setOsmUrl(String osmUrl) {
this.osmUrl = osmUrl;
}
/**
* Setter : affecte le osm attrib.
*
* @param osmAttrib le osm attrib
*/
public void setOsmAttrib(String osmAttrib) {
this.osmAttrib = osmAttrib;
}
/**
* Setter : affecte le min zoom.
*
* @param minZoom le min zoom
*/
public void setMinZoom(Integer minZoom) {
this.minZoom = minZoom;
}
/**
* Setter : affecte le max zoom.
*
* @param maxZoom le max zoom
*/
public void setMaxZoom(Integer maxZoom) {
this.maxZoom = maxZoom;
}
/**
* Setter : affecte le map x.
*
* @param mapX le map x
*/
public void setMapX(Double mapX) {
this.mapX = mapX;
}
/**
* Setter : affecte le map y.
*
* @param mapY le map y
*/
public void setMapY(Double mapY) {
this.mapY = mapY;
}
/**
* Setter : affecte le map default zoom.
*
* @param mapDefaultZoom le map default zoom
*/
public void setMapDefaultZoom(Integer mapDefaultZoom) {
this.mapDefaultZoom = mapDefaultZoom;
}
/**
* Setter : affecte le map basic layer name.
*
* @param mapBasicLayerName le map basic layer name
*/
public void setMapBasicLayerName(String mapBasicLayerName) {
this.mapBasicLayerName = mapBasicLayerName;
}
/**
* Getter : retourne le map enable on click.
*
* @return le map enable on click
*/
public Boolean getMapEnableOnClick() {
return mapEnableOnClick;
}
/**
* Setter : affecte le map enable on click.
*
* @param mapEnableOnClick le map enable on click
*/
public void setMapEnableOnClick(Boolean mapEnableOnClick) {
this.mapEnableOnClick = mapEnableOnClick;
}
}
为了管理我的组件,我还创建了一个支持bean。这是xhtml源代码:
<h:body>
<!-- INTERFACE -->
<composite:interface componentType="mapMarker">
<composite:attribute name="longitude" type="java.lang.Float" />
<composite:attribute name="latitude" type="java.lang.Float" />
<composite:attribute name="padding" type="java.lang.Integer" />
<composite:attribute name="popUpContent" type="java.lang.String" />
<composite:attribute name="maxZoom" type="java.lang.Integer" />
</composite:interface>
<!-- IMPLEMENTATION -->
<composite:implementation>
<script type="text/javascript">
//
function onPopupClick(padding, lat, lng) {
map.closePopup(clusterPopup);
if(map.getZoom() < #{cc.maxZoom}) {
var pos = map.latLngToLayerPoint(new L.LatLng(#{cc.latitude}, #{cc.longitude}));
var sw = new L.point(pos.x - padding, pos.y + padding);
sw = map.layerPointToLatLng(sw);
var ne = new L.point(pos.x + padding, pos.y - padding);
ne = map.layerPointToLatLng(ne);
map.setView(new L.LatLng(lat, lng), #{cc.maxZoom});
}
};
marker = new L.marker([#{cc.latitude}, #{cc.longitude}], {
draggable : true,
icon : cameraIcon
});
var popUpContent = '<span style="cursor:pointer;" id="#{cc.id}Link" onclick="onPopupClick(#{cc.padding},' + #{cc.latitude} + ',' + #{cc.longitude} + ')">#{cc.popUpContent}</span>';
marker.bindPopup(popUpContent);
clusterer.addMarker(marker);
</script>
</composite:implementation>
</h:body>
这是我的Backing Bean的源代码:
@FacesComponent(value = "mapMarker")
public class MapMarker extends UINamingContainer {
private static final String PADDING_PROP = "padding";
private static final String MAX_ZOOM_MARKER_PROP = "max_zoom_marker";
private Float longitude;
private Float latitude;
private String popUpContent;
private Integer padding;
private Integer maxZoom;
public MapMarker() {
padding = Integer.parseInt((String) AppUtil.getProperty(PADDING_PROP));
maxZoom = Integer.parseInt((String) AppUtil.getProperty(MAX_ZOOM_MARKER_PROP));
}
public Float getLongitude() {
return longitude;
}
public void setLongitude(Float longitude) {
this.longitude = longitude;
}
public Float getLatitude() {
return latitude;
}
public void setLatitude(Float latitude) {
this.latitude = latitude;
}
public String getPopUpContent() {
return popUpContent;
}
public void setPopUpContent(String popUpContent) {
this.popUpContent = popUpContent;
}
public Integer getPadding() {
return padding;
}
public void setPadding(Integer padding) {
this.padding = padding;
}
public Integer getMaxZoom() {
return maxZoom;
}
public void setMaxZoom(Integer maxZoom) {
this.maxZoom = maxZoom;
}
}
组件通过读取包含默认值的属性文件来设置其属性。 以下是文件的内容:
# Cartographie OpenStreetMap
map_id=map
osm_attrib=test
min_zoom=1
max_zoom=13
map_x=48.227
map_y=6.611
map_default_zoom=8
map_basic_layer_name=Basique
map_enable_on_click=true
# Cartographie OpenStreetMap
# Marqueur de caméra
padding=20
max_zoom_marker=11
# Marqueur de caméra
我有一个bean“ConsultationBean”,它处理包含地图的视图。 bean使用服务存储在ArrayList中,包含在数据库中的所有摄像机。在视图中,我通过prerenderView evet调用init方法,通过使用摄像机列表创建所有MapMarker。这是xhtml源代码:
<ui:composition template="/xhtml/common/layout.xhtml">
<ui:define name="headTitle">
<h:outputText value="#Cartographie#" />
</ui:define>
<ui:define name="header">
<ui:include src="/xhtml/common/header.xhtml">
<ui:param name="headerClass" value="banner-thin" />
</ui:include>
</ui:define>
<ui:define name="content">
<f:metadata>
<f:event type="preRenderView" listener="#{consultationBean.init}"
update="content,mapPanelGroup"></f:event>
</f:metadata>
<div id="content" class="content-home ui-corner-bottom">
<h2 class="main-title content-title light-bg light-bordered-top">
<h:outputText value="#Cartographie#" />
</h2>
<h:panelGroup id="mapPanelGroup">
<util:map id="mapContainer">
<ui:repeat var="marker" value="#{consultationBean.markers}">
<util:mapMarker/>
</ui:repeat>
</util:map>
</h:panelGroup>
</div>
</ui:define>
</ui:composition>
</html>
以下是支持bean的源代码:
/**
* Le Class ConsultationBean.
*/
@Controller
@Scope("view")
@SuppressWarnings("serial")
public class ConsultationBean implements Serializable {
/** La constante LOGGER. */
private static final Logger LOGGER = Logger.getLogger(ConsultationBean.class);
/** Le cameras. */
private List<Camera> cameras;
/** Le map. */
private UINamingContainer map;
/** Le markers. */
private List<MapMarker> markers;
/** Le camera service. */
@Autowired
private CameraService cameraService;
/**
* Initialise le.
*/
public void init() {
cameras = cameraService.findAll();
map = (UINamingContainer) FacesUtils.findComponentById(FacesContext.getCurrentInstance(), "mapContainer");
initMarkers();
}
/**
* Initialise le markers.
*/
public void initMarkers() {
markers = new ArrayList<MapMarker>();
for (Camera camera : cameras) {
MapMarker mapMarker = new MapMarker();
mapMarker.setLatitude(camera.getY());
mapMarker.setLongitude(camera.getX());
mapMarker.setPopUpContent(camera.toString());
markers.add(mapMarker);
map.getChildren().add(mapMarker);
map.getChildren().add(new HtmlInputText());
}
}
/**
* Getter : retourne le markers.
*
* @return le markers
*/
public List<MapMarker> getMarkers() {
return markers;
}
/**
* Setter : affecte le markers.
*
* @param markers le markers
*/
public void setMarkers(List<MapMarker> markers) {
this.markers = markers;
}
}
我的问题是我需要将我的属性设置为MapMarker的默认构造函数。例如,通过使用follogwing默认构造函数,我的标记将在地图上的完全相同的位置显示:
public MapMarker() {
padding = Integer.parseInt((String) AppUtil.getProperty(PADDING_PROP));
maxZoom = Integer.parseInt((String) AppUtil.getProperty(MAX_ZOOM_MARKER_PROP));
latitude = (float) 47.67876;
longitude = (float) 6.97061;
}
我想要做的是从“ConsultationBean”中获取“init”方法,因为它初始化了标记的属性,就像默认的构造函数一样。我必须使用“init”方法,因为它是我使用我的相机列表的地方。
也许我应该重新渲染视图,这就是为什么我使用了preRender事件的更新属性,但它不起作用。
如果有人有想法解决这个问题......
提前致谢。
更新1:
“ConsultationBean”中的“init”方法效率不高。实际上,我创建了“MapMarker”并将它们添加到地图中,但它们从未出现在视图中。但是,视图中包含的标记是由视图生成的标记,vi是“ui:repeat”标记:
<ui:repeat var="marker" value="#{consultationBean.markers}">
<util:mapMarker/>
</ui:repeat>
我注意到我的MapMarkers是在“ConsultationBean”之前构建的。因此,我的摄像机列表和标记列表为空,但生成的标记数量与存储在数据库中的摄像机数量相同。
我尝试使用“#{camera.code}#作为值为标记添加id,使用以下代码:
<ui:repeat var="camera" value="#{consultationBean.cameras}">
<util:mapMarker id="#{camera.code}"/>
</ui:repeat>
不幸的是它失败了,因为列表是空的。我真的很困惑于生成标记的方式。
答案 0 :(得分:1)
我终于找到了解决方案。 我所做的是我在名为“MapMarkersContainer”的视图中添加了一个附加组件。该组件包含摄像机列表,在调用MapMarker的“encodeBegin”之前填充此列表。 我修改了“MapMarker”的“encodeBegin”,以便通过“MapMarkersContainer”列表获取所有摄像机信息。
<h:body>
<!-- INTERFACE -->
<composite:interface componentType="mapMarkersContainer">
<composite:attribute name="cameras" type="java.util.List" />
</composite:interface>
<!-- IMPLEMENTATION -->
<composite:implementation>
</composite:implementation>
</h:body>
</html>
以下是Backing Bean的源代码:
@FacesComponent(value = "mapMarkersContainer")
public class MapMarkersContainer extends UINamingContainer {
private List<Camera> cameras;
private Integer currentIndex = 0;
public List<Camera> getCameras() {
currentIndex++;
return cameras;
}
public void setCameras(List<Camera> cameras) {
this.cameras = cameras;
}
public Integer getCurrentIndex() {
return currentIndex;
}
public void setCurrentIndex(Integer currentIndex) {
this.currentIndex = currentIndex;
}
}
以下是“MapMarker”中的修改:
@FacesComponent(value = "mapMarker")
public class MapMarker extends UINamingContainer {
private static final String PADDING_PROP = "padding";
private static final String MAX_ZOOM_MARKER_PROP = "max_zoom_marker";
private Float longitude;
private Float latitude;
private String popUpContent;
private Integer padding;
private Integer maxZoom;
private Camera camera;
public MapMarker() {
padding = Integer.parseInt((String) AppUtil.getProperty(PADDING_PROP));
maxZoom = Integer.parseInt((String) AppUtil.getProperty(MAX_ZOOM_MARKER_PROP));
}
@Override
public void encodeBegin(FacesContext context) throws IOException {
padding = Integer.parseInt((String) AppUtil.getProperty(PADDING_PROP));
maxZoom = Integer.parseInt((String) AppUtil.getProperty(MAX_ZOOM_MARKER_PROP));
MapMarkersContainer mapMarkersContainer = (MapMarkersContainer) FacesUtils.findComponentById(context,
"mapMarkersContainer");
Integer cameraIndex = mapMarkersContainer.getCurrentIndex();
Camera camera = mapMarkersContainer.getCameras().get(cameraIndex);
latitude = camera.getY();
longitude = camera.getX();
popUpContent = camera.toString();
setId(camera.getNom());
super.encodeBegin(context);
}
public Float getLongitude() {
return longitude;
}
public void setLongitude(Float longitude) {
this.longitude = longitude;
}
public Float getLatitude() {
return latitude;
}
public void setLatitude(Float latitude) {
this.latitude = latitude;
}
public String getPopUpContent() {
return popUpContent;
}
public void setPopUpContent(String popUpContent) {
this.popUpContent = popUpContent;
}
public Integer getPadding() {
return padding;
}
public void setPadding(Integer padding) {
this.padding = padding;
}
public Integer getMaxZoom() {
return maxZoom;
}
public void setMaxZoom(Integer maxZoom) {
this.maxZoom = maxZoom;
}
public Camera getCamera() {
return camera;
}
public void setCamera(Camera camera) {
this.camera = camera;
}
}
以下是“ConsultationBean”视图中的变化:
<ui:composition template="/xhtml/common/layout.xhtml">
<ui:define name="headTitle">
<h:outputText value="#Cartographie#" />
</ui:define>
<ui:define name="header">
<ui:include src="/xhtml/common/header.xhtml">
<ui:param name="headerClass" value="banner-thin" />
</ui:include>
</ui:define>
<ui:define name="content">
<util:mapMarkersContainer id="mapMarkersContainer"/>
<div id="content" class="content-home ui-corner-bottom">
<h2 class="main-title content-title light-bg light-bordered-top">
<h:outputText value="#Cartographie#" />
</h2>
<h:panelGroup id="mapPanelGroup">
<util:map id="mapContainer">
<ui:repeat id="repeat" var="camera" value="#{consultationBean.cameras}">
<util:mapMarker/>
</ui:repeat>
</util:map>
</h:panelGroup>
</div>
</ui:define>
</ui:composition>
</html>
以下是“ConsultationBean”的Java源代码:
/**
* Le Class ConsultationBean.
*/
@Controller
@Scope("view")
@SuppressWarnings("serial")
public class ConsultationBean implements Serializable {
/** Le cameras. */
private List<Camera> cameras;
/** Le markers. */
private List<MapMarker> markers;
private MapMarkersContainer mapMarkersContainer;
/** Le camera service. */
@Autowired
private CameraService cameraService;
/**
* Initialise le.
*
* @throws IOException
*/
@PostConstruct
public void init() throws IOException {
cameras = cameraService.findAll();
mapMarkersContainer = (MapMarkersContainer) FacesUtils.findComponentById(FacesContext.getCurrentInstance(),
"mapMarkersContainer");
mapMarkersContainer.setCameras(cameras);
}
/**
* Getter : retourne le markers.
*
* @return le markers
*/
public List<MapMarker> getMarkers() {
return markers;
}
/**
* Setter : affecte le markers.
*
* @param markers le markers
*/
public void setMarkers(List<MapMarker> markers) {
this.markers = markers;
}
public List<Camera> getCameras() {
return cameras;
}
public void setCameras(List<Camera> cameras) {
this.cameras = cameras;
}
}
现在,每个标记都有自己的ID和自己正确的值。每次使用摄像机列表的getter方法时,计数器都会更新,以便您可以使用正确的索引访问正确的摄像机。
感谢Guilherme Torres Castro的答案以及“encodeBegin”方法的发现。
答案 1 :(得分:0)
试试这个:
@Override
public void encodeBegin(FacesContext context) throws IOException{
padding = Integer.parseInt((String) AppUtil.getProperty(PADDING_PROP));
maxZoom = Integer.parseInt((String) AppUtil.getProperty(MAX_ZOOM_MARKER_PROP));
latitude = (float) 47.67876;
longitude = (float) 6.97061;
super.encodeBegin(context);
}