[GIS] Problems with features, popups and strategy with OpenLayers

featuresopenlayers-2popup

I'm trying to create a website for vehicles localization on map, but i have some problem with openlayers.
This is my first time with OL…

What I would do is:
– add and update features (my vehicles) on map. DONE
– follow only vehicles visible on screen. DONE
– add and update popup on map. PARTIAL 1
– add clustering options. NO
– remove and add features in run time. PARTIAL 2

I have a series of functions called by runtime engine for updating values.

PARTIAL 1 Popup is shown correclt
popup is shown correctly but what I want to do is update it values and position when vehicle is updated (function drawPosition) – only if popup is active –

STRATEGY
I am a little bit confused about clustering mode.
Now I have a series of png with different color (which represent the status of vehicles).
I would create groups of icons with same color, visibles only by at some zoom level.
Is it possible to create a vector (circles with different color) to represent clusters if I used PNG icons as markers?
Or i must create a big PNG that represent the cluster?

PARTIAL 2
A user can change vehicles shown on map with a jquery dialog.
JS call: pulisiMappa() to clean map from all features and call again drawPosition() to add vehicles choosed by user.

It works great but with my code i lose something.
I don't know if with destroyFeatures() I lose the event.register on l_elements (my vector layer) or I lose something about SelectFeature

Where's the problem?

this is my code:


var map;
var justLoaded = 0
var points = [];
var storico = { path: null, pos: null, pts: [], viaggio: { viaggio: null, start: null, stop: null} };
var eventPoints = [];
var select_feature_control;
var l_elements, m_osm, m_google_str, m_google_sat, m_google_ril, m_google_satH, m_ovi, m_base, m_base_et;

// WGS84 -> PTV_MERCATOR
OpenLayers.Projection.addTransform("EPSG:4326", "PTV_MERCATOR", function (point) { var RPI = Math.PI * 6371000.0; var x = point.x * RPI / 180; var y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180); y = y * RPI / 180; point.x = x; point.y = y; });
// PTV_MERCATOR -> WGS84
OpenLayers.Projection.addTransform("PTV_MERCATOR", "EPSG:4326", function (point) { var RPI = Math.PI * 6371000.0; var lon = (point.x / RPI) * 180; var lat = (point.y / RPI) * 180; lat = 180 / Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2); point.x = lon; point.y = lat; });

function initMap() {

    map = new OpenLayers.Map({
        div: "mapContainer",
        projection: new OpenLayers.Projection("EPSG:4326"),
        displayProjection: new OpenLayers.Projection("EPSG:4326"),
        controls: [
            new OpenLayers.Control.Attribution(),
            new OpenLayers.Control.TouchNavigation({
                dragPanOptions: {
                    interval: 100,
                    enableKinetic: true
                }
            }),
            new OpenLayers.Control.ScaleLine({}),
            new OpenLayers.Control.LayerSwitcher(),
            new OpenLayers.Control.NavToolbar(),
            new OpenLayers.Control.KeyboardDefaults({ autoActivate: true })
        ],
       units: "m",
       numZoomLevels: 20,
       maxResolution: 9783.939619140625, /*156543.0399,*/
       minResolution: 0.5971642833948135,
       maxExtent: new OpenLayers.Bounds(-19900000.0, -7300000.0, 19900000.0, 18600000.0),
       restrictExtent: new OpenLayers.Bounds(-19900000.0, -7300000.0, 19900000.0, 18600000.0)
    });

   var style = new OpenLayers.Style({
       pointRadius: "${radius}",
       fillColor: "#ffcc66",
       fillOpacity: 0.8,
       strokeColor: "#cc6633",
       strokeWidth: 2,
       strokeOpacity: 0.8,
       externalGraphic: "${icona_elemento}",
       graphicWidth: 38,
       graphicHeight: 38,
       rotation: "${angle}",
       fillOpacity: 1,
       graphicZIndex: 10,
       label: "${nome_elemento}",
       labelAlign: "cb",
       labelYOffset: -24,
       fontOpacity: 1,
           fontWeight: 600,
           fontColor: "#000",
           fontSize: "10px",
           fontFamily: "\"Lucida Grande\", \"Lucida Sans Unicode\", Helvetica, Arial, Verdana, sans-serif"
       }, {
       context: {
           externalGraphic: null,
           radius: function (feature) {
               return Math.min(feature.attributes.count, 7) + 3;
           }
       }
   });


    m_osm = new OpenLayers.Layer.OSM();
    m_google_ril = new OpenLayers.Layer.Google("Google Rillievo",{ type: google.maps.MapTypeId.TERRAIN });
    m_google_str = new OpenLayers.Layer.Google("Google Stradale", { numZoomLevels: 20, isBaseLayer: true, sphericalMercator: true });
    m_google_satH = new OpenLayers.Layer.Google("Google Satellite Ibrida",{ type: google.maps.MapTypeId.HYBRID, numZoomLevels: 20 });
    m_google_sat = new OpenLayers.Layer.Google("Google Satellite", { type: google.maps.MapTypeId.SATELLITE, numZoomLevels: 20, sphericalMercator:true });
    m_ovi = new OpenLayers.Layer.XYZ("Nokia Ovi Maps", 
            ["http://a.maptile.maps.svc.ovi.com/maptiler/maptile/newest/normal.day/${z}/${x}/${y}/256/png8", 
             "http://b.maptile.maps.svc.ovi.com/maptiler/maptile/newest/normal.day/${z}/${x}/${y}/256/png8"],
             { transitionEffect: 'resize', sphericalMercator: true, numZoomLevels: 18 });


    l_elements = new OpenLayers.Layer.Vector("Veicoli", {
            styleMap: new OpenLayers.StyleMap({
                "default": style,
                "select": {
                    cursor: "hand",
                    graphicWidth: 40,
                    graphicHeight: 40,
                    graphicZIndex: 11,
                    fillColor: "#8aeeef",
                    strokeColor: "#32a8a9"
                }
            }), /*
            strategies: [
              new OpenLayers.Strategy.Refresh(),
              new OpenLayers.Strategy.Cluster()
            ],*/
            renders: ['canvas', 'SVG', 'VML'],
            rendererOptions: { zIndexing: true }
        }
    );

    map.addLayers([m_osm, m_google_str, m_google_sat, m_ovi, m_google_ril, m_google_satH, l_elements]);

    //setto il clip rect e lo zoom
    var curClipRect = parent.getCartografiaClipRect();
    var myClipRect = { left: curClipRect.left / 100000, top: curClipRect.top / 100000, right: curClipRect.right / 100000, bottom: curClipRect.bottom / 100000 }
    var theRect = new OpenLayers.Bounds();

    theRect.extend(new OpenLayers.LonLat(myClipRect.left, myClipRect.top).transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject()));
    theRect.extend(new OpenLayers.LonLat(myClipRect.right, myClipRect.bottom).transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject()));
    theRect.toBBOX();

    map.zoomToExtent(theRect, true);


    select_feature_control = new OpenLayers.Control.SelectFeature(l_elements, { multiple: false, toggle: true, hover: false });
    map.addControl(select_feature_control);
    select_feature_control.activate();

    l_elements.events.on({
        'featureselected': function (evt) {
            var feature = evt.feature;
            popup = new OpenLayers.Popup.FramedCloud("featurePopup",
                            feature.geometry.getBounds().getCenterLonLat(),
                            new OpenLayers.Size(280, 63), feature.attributes.description, null, true, select_feature_control.unselect(feature));
            feature.popup = popup;
            popup.feature = feature;
            map.addPopup(popup, true);
            $('a[id$="_bubbleDetail"]').click(function () {
                var myid = this.id.replace("_bubbleDetail", "");
                if (parent.$("#cruscotto").dialog("isOpen") == false) { parent.$("#cruscotto").dialog("open"); }
                parent.$(".box_container").not("#" + myid + "_boxContainer").removeClass("active");
                parent.$("#" + myid + "_boxContainer").addClass("active");
                parent.handleBubbleDetail(myid);
                map.removePopup(feature.popup);
                feature.popup.destroy();
                feature.popup = null;
            });
        },
        'featureunselected': function (evt) {
            var feature = evt.feature;
            if (feature.popup) {
                map.removePopup(feature.popup);
                feature.popup.destroy();
                feature.popup = null;
            }
        }
    });


    justLoaded = 1;
    parent.rebuildScene();
    parent.setMapLoaded();

    if (parent.document.location.href.indexOf("listaveicoli") >= 0) {
        map.events.register('zoomend', this, changeZoomHandler);
        map.events.register('moveend', this, changeZoomHandler);
    }
}



function drawPosition(posInfo) {

    // new feature
    if (points["_" + posInfo.id] == undefined) {
        var newPos = new OpenLayers.Feature.Vector();
        var obj = { id: posInfo.id, follow: posInfo.follow, newpos: 1, _pt: newPos, _lb: null, loaded: 0 };
        points["_" + posInfo.id] = obj;
    }

    if (posInfo.ico.indexOf("old_style")) {
        var colore = posInfo.ico.split("_");
        if (parseInt(posInfo.dir)  0)
                    pts = pts + ",";
                pts = pts + myId;
            }
            else
                points[pos].follow = 0;
        }
    }
    var todo = 1;
    parent.setMezziToFollow(pts);
};


/* -----------------------------------------------------------------------------------------------------------------------------  */



//map clean
function pulisciMappa() {
    for (var myId in points) {
        var tmpInfo = { id: myId.replace("_", "") }
        removePosition(tmpInfo);
    }
    points = [];
//    deleteViaggio();
//    deletePosizioneStorico();
//    deleteStorico();
    //    deleteEventi();
}

//remove features
function removePosition(posInfo) {
    //id veicolo
    var myId = posInfo.id;
    var _punto = points["_" + posInfo.id]._pt;
    if (points["_" + posInfo.id].loaded == 1) {
        l_elements.destroyFeatures(_punto);
    }
    points["_" + posInfo.id]._pt = null;
    points["_" + posInfo.id]._lb = null;
    points["_" + posInfo.id].loaded = 0;
}



I'll hope everything is clear 😉


EDIT 1: about partial 1 and 2…

it's me again..

Thanks Aragon, with your code now I can update popup content and its position.

I added this code to drawPosition() function:

    if(map.popups.length > 0) {
     if(_punto.id == map.popups[0].id) {
         map.popups[0].setContentHTML(posInfo.bubble);
         map.popups[0].lonlat.lon = pointCoord.x;
         map.popups[0].lonlat.lat = pointCoord.y;
         map.popups[0].updatePosition();
     }
    }

and everything works fine.

Now I m trying to fix the "event listner" on features when I update vehicles to show.

When I select some vehicles from a jquery dialog, a backend function cleans the vector layer (l_elements) and when layer is clean, there is a "for" cycle that make a call of drawPosition() to add a new feature for every vehicle selected.

Everything works good except the "event listener" over this features.

If I click on every features nothing happens.. 🙁

I have this code in init() function:

    select_feature_control = new OpenLayers.Control.SelectFeature(l_elements, { multiple: false, toggle: true, hover: false });
    map.addControl(select_feature_control);
    select_feature_control.activate();

    l_elements.events.on(
    {
        'featureselected': function (evt) {
            alert("ciao");
            var feature = evt.feature;
            popup = new OpenLayers.Popup.FramedCloud("featurePopup",
                            feature.geometry.getBounds().getCenterLonLat(),
                            new OpenLayers.Size(280, 63), feature.attributes.description, null, true, select_feature_control.unselect(feature));
            feature.popup = popup;
            popup.feature = feature;
            popup.id = feature.id;
            map.addPopup(popup, true);
            $('a[id$="_bubbleDetail"]').click(function () {
                var myid = this.id.replace("_bubbleDetail", "");
                if (parent.$("#cruscotto").dialog("isOpen") == false) { parent.$("#cruscotto").dialog("open"); }
                parent.$(".box_container").not("#" + myid + "_boxContainer").removeClass("active");
                parent.$("#" + myid + "_boxContainer").addClass("active");
                parent.handleBubbleDetail(myid);
                map.removePopup(feature.popup);
                feature.popup.destroy();
                feature.popup = null;
            });
        },
        'featureunselected': function (evt) {
            var feature = evt.feature;
            if (feature.popup) {
                map.removePopup(feature.popup);
                feature.popup.destroy();
                feature.popup = null;
            }
        }
    });

Should I move it in drawPosition function?


EDIT 2:

PARTIAL 2 is solved:
I made this changes to my code:

drawPosition()

//_punto.id = posInfo.id;
    _punto.attributes = {
                angle: posInfo.dir,
                title: posInfo.label,
                id: posInfo.id,
                description: posInfo.bubble,
                icona_elemento: icona_elemento,
                nome_elemento: posInfo.label
    };

init()

        'featureselected': function (evt) {
            var feature = evt.feature;
            popup = new OpenLayers.Popup.FramedCloud("featurePopup",
                            feature.geometry.getBounds().getCenterLonLat(),
                            new OpenLayers.Size(280, 63), feature.attributes.description, null, true, select_feature_control.unselect(feature));
            feature.popup = popup;
            popup.feature = feature;
            popup.id = feature.attributes.id;
            map.addPopup(popup, true);
        },

And everything works good 😉

So..
– partial 1 is ok
– partial 2 is now ok

Best Answer

you can catch your opened(active) popup with this way:

map.popups[a]

you can change so update its content:

map.popups[a].contentHTML = 'Test Content';
map.popups[a].redraw();

or

map.popups[a].setContentHTML = 'Test Content'

you can show or hide your popup:

map.popups[a].show();
map.popups[a].hide();

you can update position of your popup:

map.popups[a].lonlat.lon = 10;
map.popups[a].lonlat.lat = 10;
map.popups[a].updatePosition();

for openlayers cluster you should check out Cluster Strategy Example here. Only one thing you should do is that define fillColor or externalGraphic for your cluster.

          var style = new OpenLayers.Style({
                pointRadius: "${radius}",
                fillColor: "${getColor}",
                fillOpacity: 0.8,
                strokeColor: "#cc6633",
                strokeWidth: 2,
                strokeOpacity: 0.8
            }, {
                context: {
                    radius: function(feature) {
                        return Math.min(feature.attributes.count, 7) + 3;
                    },
                    getColor: function(feature) {
                        return // Your color selection logic;
                    }
                }
            });

As the last question, i dont understand very well what you want? but try to use removeFeatures instead of destroyFeatures....

i hope it helps you...