OpenLayers Add-on: Immediate VectorModifiedEvent

Hello

I noticed that the vopenlayers add-on only delivers the VectorModifiedEvent when OpenLayers triggers the “afterfeaturemodified” event. As a result, I can only get notified on the server-side when the modified vector gets unselected, i.e. I have to either click on an empty region on the map or click another Feature.

Now let’s suppose that I have PointVectors and I would like to get notified as soon as any of them is moved, i.e. I do not want another click somewhere else to get the VectorModifiedEvent. (I.e. drag the vector, move it, release it and the event should fire.)

How could I achieve this?

I have looked into the add-on’s code and noticed that although VVectorLayer handles both “featuremodified” and “afterfeaturemodified”, it is only “afterfeaturemodified”, which does an immediate request to the server-side by issuing a sendPendingVariableChanges() call.

vectors.registerHandler("afterfeaturemodified", new GwtOlHandler() {
                public void onEvent(JsArray arguments) {
                    client.sendPendingVariableChanges();
                }
            });

The “featuremodified” handler behaves as if the layer was not “immediate”. However, there is a

client.sendPendingVariableChanges();

call commented out in getFeatureModifiedListener().

What is the reason for that? Would it be difficult to get the “featuremodified” events “immediately” on the server?

Thanks for any answers in advance,
Richie.

I have experimented with making the VectorModifiedEvent immediate and found the following:

I tried to set the “immediate” parameter to true in VVectorLayer#getFeatureModifiedListener():


    private GwtOlHandler getFeatureModifiedListener() {
        if (_fModifiedListener == null) {
            _fModifiedListener = new GwtOlHandler() {

                public void onEvent(JsArray arguments) {
                    if (!updating && drawingMode != "NONE") {
                        ...
                                if (vector == modifiedFeature) {
                                    client.updateVariable(paintableId, "modifiedVector", next, true); // << this call is set immediate
                                    break;
                                }
                            }
                        }
                    }
                }
            };
        }
        return _fModifiedListener;
    }

However, this does not work in itself, because there is - I think - a bug in the current version (0.9.1): when the UIDL response is returned after handling the VectorModifiedEvent on the server-side, VAbstractVector does the following:


    public void updateFromUIDL(UIDL childUIDL, final ApplicationConnection client) {
        ...

        if (vector != null) {
            getLayer().removeFeature(vector);  // (1)
        }

        //
        updateAttribuets(childUIDL, client);

        updateVector(childUIDL, client);  // (2)
        if (childUIDL.hasAttribute("style")) {
            intent = childUIDL.getStringAttribute("style");
            getVector().setRenderIntent(intent);
        }
        updateStyle(childUIDL, client);

        getLayer().addFeature(vector); // (3)
    }

It issues a VectorLayer#removeFeature(…) in (1), which - as far as I know - removes the Vector from the layer and from the layer’s selectedFeatures array but does not destroy the Vector. I.e. the Vector disappears from the map but the object remains around. In (2) updateVector(…) creates a new Vector and line (3) adds the new Vector to the layer. As a result, the new Vector appears on the map. However, when I click on another Vector, OpenLayers wants to unselect the removed Vector and its SelectFeature#clickFeature(…) method crashes with a “NullPointerException”.

I think it would be better to separate the “create” and “update” logic in VAbstractVector#updateFromUIDL(…). Something like this:


    public void updateFromUIDL(UIDL childUIDL,
            final ApplicationConnection client) {
        ...

        boolean update = true;
        if (vector != null) {
        	getLayer().eraseFeature(vector);
        } else {
        	update = false;
        }

        //
        updateAttribuets(childUIDL, client);

        createOrUpdateVector(childUIDL, client);
        if (childUIDL.hasAttribute("style")) {
            intent = childUIDL.getStringAttribute("style");
            getVector().setRenderIntent(intent);
        }
        updateStyle(childUIDL, client);

        if (update) {
        	getLayer().drawFeature(vector);
        } else {
        	getLayer().addFeature(vector);
        }
    }

I.e., the old logic would remain when a new Vector is to be created, but when we are updating an existing one then we would just erase it from the map, update its properties and then redraw it. For VPointVector, createOrUpdateVector(…) may look like the one below:


    @Override
    protected void createOrUpdateVector(UIDL childUIDL, ApplicationConnection client) {
        Projection mapProjection = getMap().getProjection();

        Point p = Point.create(childUIDL.getStringArrayAttribute("points")[0]
);
        Util.browserDebugger();
        VConsole.log(getProjection().toString() +" -> "+ mapProjection.toString());
        VConsole.log(p.toString());
        p.transform(getProjection(), mapProjection);
        VConsole.log(p.toString());

        JavaScriptObject style = null;
        ValueMap attributes = getAttributes();
        if (vector == null) {
        	vector = Vector.create(p, attributes, style);
        } else {
        	vector.setGeometry(p);
        	vector.setStyle(style);
        	vector.setAttributes(attributes);
        }

    }

In addition we need some trivial JSNI methods for VectorLayer#eraseFeature(…),VectorLayer#drawFeature(…), Vector#setgeometry(…) and Vector#setAttributes(…).

Obviously, this is just a quick and dirty modification example of the existing code to make immediate VectorModifiedEvents work.

Do you think it is OK? Or am I overlooking something?

Cheers,
Richie.

Hi,

I remember I created the initial version of the vector editing to be always non-immediate. That was the best option for my case, but I can see it is not always the desired behavior. Have you created an issue about this to the google code page? If you think you have solved the issue, you can attach a path to the issue and I’ll review it next time I work on this project.

cheers,
matti

Hi

I think I have a solution for this and for
Issue #37
, as well. I would be glad to contribute it back to the project if you review and accept it.

I am afraid, I will only have time to clean up my solution and create a patch out of it during the weekend, but I will do it.

Thanks,
Richie.

Sounds great! Thanks in advance.

cheers,
matti

Hi

I attached a patch to
Issue #37
. Please, take a look at it.

Cheers,
Richie.

Cool, I’ll try to find a moment to work on OpenLayers wrapper this week, there are several other pending reviews and fixes to be done.

cheers,
matti