JSF2とPrimefacesをWebFrameworksとして使用してJavaWebアプリケーションを開発しています。私のアプリケーションはカメラの管理を目的としています。カメラに関するすべてのデータはPostgreSQLデータベースに保存されます。
LeafletのJavascriptAPIを使用して動的マップを表示するJSFビューがあります。このようなマップの例は、次のリンクから確認できます:リーフレットの例。
要約すると、私の地図はカメラのマーカーを表示することを目的としています。マーカーの位置は、カメラの属性を使用して生成されます。
マップを処理するための複合コンポーネントとそのバッキング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>
そして、これが私のバッキング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のデフォルトコンストラクターに設定する必要があることです。たとえば、次のデフォルトコンストラクターを使用すると、マーカーはマップ上のまったく同じポイントに表示されます。
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イベントのupdate属性を使用した理由ですが、機能しません。
誰かがこの問題を解決するアイデアを持っているなら...
前もって感謝します。
アップデート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>
リストが空であるため、残念ながら失敗しました。私は自分のマーカーを生成する方法に本当に固執しています。