1

我正在开发一个使用 JSF2 和 Primefaces 作为 WebFrameworks 的 Java Web 应用程序。我的应用程序旨在管理摄像机。所有有关摄像机的数据都存储在 PostgreSQL 数据库中。
我有一个 JSF 视图,在其中我使用 Leaflet 的 Javascript API 显示动态地图。您可以通过以下链接查看此类地图的示例:Leaflet example

总而言之,我的地图旨在显示相机的标记。标记的位置是使用相机的属性生成的。
我创建了一个复合组件及其支持 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() &lt; #{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 事件调用 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 事件的更新属性的原因,但它不起作用。

如果有人有解决这个问题的想法......

提前致谢。

更新 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>

不幸的是它失败了,因为列表是空的。我真的很坚持生成我的标记的方式。

4

2 回答 2

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”方法的发现。

于 2012-09-11T08:01:55.030 回答
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);
}
于 2012-09-10T12:53:33.977 回答