I want to show a custom tooltip (popup) when user hovers over a Vector Feature on the GWT-openlayers map. I know that SelectFeature.setHover() will allow me to do this but that will also select the feature which i dont want to have.
it is like, when the user hovers, tooltip must be shown and when he clicks on the feature, then selection muse happen.
how can this be achieved?
Regards
Jatin
Check out this code.
This will also be added to our showcase but we have a little problem with the server at the moment. Will give a shout here when it is uploaded.
Note that the order in which you add the SelectFeature controls is important.
public void buildPanel() {
// create some MapOptions
MapOptions defaultMapOptions = new MapOptions();
defaultMapOptions.setNumZoomLevels(16);
// Create a MapWidget
final MapWidget mapWidget = new MapWidget("500px", "500px",
defaultMapOptions);
// Create an EmptyLayer as base layer
EmptyLayer.Options emptyLayerOptions = new EmptyLayer.Options();
emptyLayerOptions.setAttribution("EmptyLayer (c) GWT-Openlayers");
emptyLayerOptions.setIsBaseLayer(true); // make it a baselayer.
EmptyLayer emptyLayer = new EmptyLayer("Empty layer", emptyLayerOptions);
mapWidget.getMap().addLayer(emptyLayer);
// Add a clickable vectors to the map
// create a layer to add the vectors to
final Vector vectorLayer = new Vector("Vectorlayer");
mapWidget.getMap().addLayer(vectorLayer);
// SelectFeature control to capture clicks on the vectors
final SelectFeature selectFeature = new SelectFeature(vectorLayer);
selectFeature.setAutoActivate(true);
// SelectFeature control to capture hover on the vectors
SelectFeatureOptions selectFeatureHoverOptions = new SelectFeatureOptions();
// use the tempory style to be defined in the StyleMap
selectFeatureHoverOptions.setRenderIntent(RenderIntent.TEMPORARY);
selectFeatureHoverOptions.setHighlightOnly(true);
selectFeatureHoverOptions.setHover();
SelectFeature selectHoverFeature = new SelectFeature(vectorLayer,
selectFeatureHoverOptions);
selectHoverFeature.setClickOut(false);
selectHoverFeature.setAutoActivate(true);
mapWidget.getMap().addControl(selectHoverFeature);
mapWidget.getMap().addControl(selectFeature);
// Define a style for the vectors
Style style = new Style();
style.setFillColor("red");
style.setStrokeColor("green");
style.setStrokeWidth(2);
style.setFillOpacity(0.9);
style.setPointRadius(30);
Style selectedStyle = new Style();
selectedStyle.setFillColor("yellow");
selectedStyle.setStrokeColor("yellow");
selectedStyle.setStrokeWidth(2);
selectedStyle.setFillOpacity(0.9);
selectedStyle.setPointRadius(30);
Style hoverStyle = new Style();
hoverStyle.setFillColor("blue");
hoverStyle.setStrokeColor("pink");
hoverStyle.setStrokeWidth(2);
hoverStyle.setFillOpacity(0.9);
hoverStyle.setPointRadius(30);
StyleMap styleMap = new StyleMap(style, selectedStyle, hoverStyle);
vectorLayer.setStyleMap(styleMap);
// Add a point
Point point = new Point(146.7, -41.8);
final VectorFeature pointFeature = new VectorFeature(point);
vectorLayer.addFeature(pointFeature);
// capture clicks on the vectorlayer
vectorLayer
.addVectorFeatureSelectedListener(new VectorFeatureSelectedListener() {
public void onFeatureSelected(
FeatureSelectedEvent eventObject) {
Window.alert("The vector is now selected.\nIt will get de-selected when closing this popup.");
selectFeature.unSelect(eventObject.getVectorFeature());
}
});
// Attach a popup to the point, we use null as size cause we set
// autoSize to true
// Note that we use FramedCloud... This extends a normal popup and
// creates is styled as a baloon
// We want to display this popup on hover, and hide it when hovering
// ends
final Popup popup = new FramedCloud("id1",
pointFeature.getCenterLonLat(), null,
"<h1>Some popup text</H1><BR/>And more text", null, false);
popup.setPanMapIfOutOfView(true); // this set the popup in a strategic
// way, and pans the map if needed.
popup.setAutoSize(true);
pointFeature.setPopup(popup);
// capture hover by adding a listener to the control, and display the
// popup
selectHoverFeature
.addFeatureHighlightedListener(new FeatureHighlightedListener() {
public void onFeatureHighlighted(VectorFeature vectorFeature) {
mapWidget.getMap().addPopup(vectorFeature.getPopup());
}
});
// capture unhover, and remove popup
selectHoverFeature
.addFeatureUnhighlightedListener(new FeatureUnhighlightedListener() {
public void onFeatureUnhighlighted(
VectorFeature vectorFeature) {
mapWidget.getMap()
.removePopup(vectorFeature.getPopup());
}
});
// Center and zoom to a location
mapWidget.getMap().setCenter(new LonLat(146.7, -41.8), 6);
contentPanel
.add(new HTML(
"<p>This example shows how to add a Vector (point) to map, and do some action when hovering, and another when clicking.</p>"
+ "<p>"
+ "<LI>Hover over the point. This will cause a popup to show, and change the style of the point to the temporary style.</LI>"
+ "<LI>Then when you click the Vector gets selected, gets another style, and a Window.alert is displayed.</LI>"
+ "<LI>When closing the Window.alert, the Vector gets de-selected.</p>"));
contentPanel.add(mapWidget);
initWidget(contentPanel);
mapWidget.getElement().getFirstChildElement().getStyle().setZIndex(0);
}
You must indeed use a SelectFeature to achieve this.
The secret is that you must pass SelectFeatureOptions with highlight only enabled.
Something like
SelectFeatureOptions selectFeatureOptions = new SelectFeatureOptions();
selectFeatureOptions.setHighlightOnly(true);
SelectFeature selectFeature = new SelectFeature(vectorLayer,selectFeatureOptions);
Related
I have a smartGwt DynamicForm with a FormItem
FormItem item = createTextItem();
form.setFields(item);
After creating the and setting fields, I need to dynamically set an editor type for the item. I have to do it dynamically based on some conditions.
I'm calling item.setEditorType(new PasswordItem());
just after I call form.editRecord(record); so that the new editor type should appear. But it is not working.
Tried calling item.redraw() and is not working.
My goal is to set the editor type dynamically based on the record that is edited.Please help.
Try with Custom Data Binding (see page 23 for more details). What you tried won't work, AFAIK, because the ListGridField has already been created with the initial custom editor, and it can't be changed dynamically with setEditorCustomizer.
Take a look at this sample (based on this showcase demo), which does what you want to do to the password field when it is being edited in the DynamicForm, and after the changes have been saved (please pay attention to the comments, as without some of these settings it won't work as expected):
public void onModuleLoad() {
final DataSource dataSource = ItemSupplyLocalDS.getInstance();
final DynamicForm form = new DynamicForm();
form.setIsGroup(true);
form.setNumCols(4);
form.setDataSource(dataSource);
// very important for not having to set all fields all over again
// when the target field is customized
form.setUseAllDataSourceFields(true);
final ListGrid listGrid = new ListGrid();
listGrid.setWidth100();
listGrid.setHeight(200);
listGrid.setDataSource(dataSource);
listGrid.setAutoFetchData(true);
IButton editButton = new IButton("Edit");
editButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
form.editRecord(listGrid.getSelectedRecord());
// when the button is clicked, the password field is rendered with
// a plain text item editor, for easy verification of values entered
FormItem passwordField = new FormItem("passwordFieldName");
passwordField.setEditorProperties(new TextItem());
form.setFields(passwordField);
form.markForRedraw();
}
});
IButton saveButton = new IButton("Save");
saveButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
form.saveData();
// when the button is clicked, the password field is rendered with
// a password editor, for added privacy/security
FormItem passwordField = new FormItem("passwordFieldName");
passwordField.setEditorProperties(new PasswordItem());
form.setFields(passwordField);
form.markForRedraw();
}
});
VLayout layout = new VLayout(15);
layout.setWidth100();
layout.setHeight100();
layout.addMember(listGrid);
layout.addMember(editButton);
layout.addMember(form);
layout.addMember(saveButton);
layout.draw();
}
I'm trying to add new labels to a panel and this is when a button is clicked, in fact the number of labels is unknowing because my application consists in extracting some informations from a file and then display each information in a label so i have to upload the file and then extract the informations, i created an uploadfile and i'm able to extract the informations but i face a problem to display each information in its label, i can't create many labels and then with label.settext() make each information in its label beacuase the number of labels/informations is variable.
So can you advice/help me so i can make this working.
Best regards.
If you get the result from an Array for example you can do like this:
String[] data; //You can add you data here
addButton.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
for (String s : data) {
RootPanel.get().add(new Label(s));
}
}
});
That way you can add as much Labels as you want
You can create a variable number of Labels with a LinkedList.
int count = x ; //Quantity of labels you need;
LinkedList<Label> labelList = new LinkedList<Label>();
for (int i = 0; i < count ;i++)
{
Label tmpLabel = new Label();
tmpLabel.setText(STUFF) //Here you have to set your content
labelList.add(tmpLabel);
}
// Now we add the Labels to the Panel
for (int ind = 0; ind < labelList.size() ;ind++)
{
panel.add(labelList.get(ind)); //panel is the panel you show
}
If you don't have to access the labels later, you don't need the LinkedList and could add them direct to your panel.
You didnt say how you exactly attach the Labels, but if you use a Grid you have to set the size of it depending on your information.
Based on the GXT showcase example 'Paging BeanModel Grid' I try to reload the grid when I have done a modifiaction to my data source. I defined the loader like that:
final BasePagingLoader<PagingLoadResult<ModelData>> loader =
new BasePagingLoader<PagingLoadResult<ModelData>>(proxy, new BeanModelReader());
loader.setRemoteSort(true);
The data loads correctly.
When I do:
loader.load();
My paging toolbar just freezes and goes disabled, the grid freezes too and displays what appears to be a loading mask.
I tried to add some events to force a double reload without luck:
grid.addListener(Events.Attach, new Listener<GridEvent<ModelData>>() {
public void handleEvent(GridEvent<ModelData> be) {
loader.load();
}
});
I tried to use the reconfigure(store,cm) option as well and same result.
Any help?
Thanks,
Jordi.
The loading bar grays out so you can see it is working, and the grid may also have a loading message. The code that is working is the server, probably preparing the items.
Set logging messages in your server code (RPC servlet probably), at the beginning and at the end of the call, to see how long they take to run. That is likely where the actual 'freeze' is actually happening, based on the info in your question.
There could be a pause in the browser after that time as well, but in that case the loading circle would stop moving.
To me there is a bug on the grid controller:
Code details:
private final BasePagingLoader<PagingLoadResult<ModelData>> loader;
private Grid<ModelData> grid;
[...]
public ListUsersView(RpcProxy<PagingLoadResult<UserTableEntryBean>> proxy) {
// Create loader
loader = new BasePagingLoader<PagingLoadResult<ModelData>>(proxy, new BeanModelReader());
loader.setRemoteSort(true);
// Create store
store = new ListStore<ModelData>(loader);
FlowLayout layout = new FlowLayout();
layout.setMargins(new Margins(3, 0, 0, 0));
this.setLayout(layout);
final PagingToolBar toolBar = new PagingToolBar(50);
toolBar.bind(loader);
List<ColumnConfig> columns = new ArrayList<ColumnConfig>();
columns.add(new ColumnConfig(UserTableEntryBean.Fields.username.name(), "Username", 100));
columns.add(new ColumnConfig(UserTableEntryBean.Fields.email.name(), "E-mail", 200));
ColumnConfig date = new ColumnConfig(UserTableEntryBean.Fields.creationDate.name(), "Creation date", 100);
date.setDateTimeFormat(DateTimeFormat.getFormat("dd/MM/y"));
columns.add(date);
ColumnModel cm = new ColumnModel(columns);
grid = new Grid<ModelData>(store, cm);
grid.setLoadMask(true);
grid.setBorders(true);
grid.setAutoExpandColumn(UserTableEntryBean.Fields.creationDate.name());
[...]
}
public boolean refreshTable() {
return loader.load();
}
I am having issues with OpenLayers and unregistering the click events that are added to a layer. Basically, what I need to do is this:
When a user clicks on a marker, they get a bubble that has an "edit" link in it. The user clicks that and it creates a new layer on the map and then registers a click event to the map waiting for the user to click on the map. When they click somewhere on the map, it then moves the marker to where they clicked. This all works perfectly.
However, the issue is when the user clicks to edit the marker and then clicks on a button OUTSIDE OF THE MAP to cancel the action and NOT move the marker, the unregister of the click event doesn't work. They can still click on the map and it moves the marker.
Here is a sample of the code:
function move_marker(marker) {
lmLayer = new OpenLayers.Layer.Markers("Landmark Creation",{displayInLayerSwitcher: false});
map.addLayer(lmLayer);
map.events.register("click", lmLayer, function(evt){
var pixel = new OpenLayers.Pixel(evt.clientX,evt.clientY);
position = map.getLonLatFromPixel(pixel);
marker.lonlat = pixel;
marker.moveTo(pixel);
marker.draw();
lmLayer.redraw();
OpenLayers.Event.stop(evt);
});
}
function cancel_move() { // this function is triggered by a button outside of the map element
lmLayer = map.getLayersByName('Landmark Creation');
lmLayer[0].events.unregister("click");
map.events.unregister("click");
map.removeLayer(lmLayer[0]);
}
As you can see in the cancel function, I am getting the layer by the layer name, which according to the console.log it is finding at index 0. I added the unregister to the lmLayer in hopes that would help, but so far, no luck. Then on the map element I add the unregister call and then finally I remove that new layer because we don't want it interfering.
I'd appreciate some feedback on this. I'm losing my mind.
Thanks!
I think you need to tell OpenLayers which click event you want it to unregister:
var method = function() {
// Do stuff...
}
map.events.register('click', map, method);
map.events.unregister('click', map, method);
According to the OpenLayers.Events source it checks whether the scope and the method is present in the listener stack:
unregister: function (type, obj, func) {
if (obj == null) {
obj = this.object;
}
var listeners = this.listeners[type];
if (listeners != null) {
for (var i=0, len=listeners.length; i<len; i++) {
HERE --> if (listeners[i].obj == obj && listeners[i].func == func) { <-- HERE
listeners.splice(i, 1);
break;
}
}
}
},
I hope that works for you :)
I have the feature ID, I can grab the marker layer on GeoRSS loadend, but I'm still not sure how to cause the popup to appear programmatically.
I'll create the popup on demand if that's necessary, but it seems as though I should be able to get the id of the marker as drawn on the map and call some event on that. I've tried using jQuery and calling the $(marker-id).click() event on the map elements, but that doesn't seem to be working. What am I missing?
Since I was asked for code, and since I presumed it to be boilerplate, here's where I am so far:
map = new OpenLayers.Map('myMap');
map.addLayer(new OpenLayers.Layer.OSM());
map.addLayer(new OpenLayers.Layer.GeoRSS(name,url));
//I've done some stuff as well in re: projections and centering and
//setting extents, but those really don't pertain to this question.
Elsewhere I've done a bit of jQuery templating and built me a nice list of all the points that are being shown on the map. I know how to do a callback from the layer loadend and get the layer object, I know how to retrieve my layer out of the map manually, I know how to iter over the layers collection and find my layer. So I can grab any of those details about the popup, but I still don't know how to go about using the built-in methods of the DOM or of this API to make it as easy as element.click() which is what I would prefer to do.
You don't have to click the feature to open a popup.
First you need a reference to the feature from the feature id. I would do that in the loadend event of the GeoRSS layer, using the markers property on the layer.
Assuming you have a reference to your feature, I would write a method which handles the automatic popup:
var popups = {}; // to be able to handle them later
function addPopup(feature) {
var text = getHtmlContent(feature); // handle the content in a separate function.
var popupId = evt.xy.x + "," + evt.xy.y;
var popup = popups[popupId];
if (!popup || !popup.map) {
popup = new OpenLayers.Popup.Anchored(
popupId,
feature.lonlat,
null,
" ",
null,
true,
function(evt) {
delete popups[this.id];
this.hide();
OpenLayers.Event.stop(evt);
}
);
popup.autoSize = true;
popup.useInlineStyles = false;
popups[popupId] = popup;
feature.layer.map.addPopup(popup, true);
}
popup.setContentHTML(popup.contentHTML + text);
popup.show();
}
fwiw I finally came back to this and did something entirely different, but his answer was a good one.
//I have a list of boxes that contain the information on the map (think google maps)
$('.paginatedItem').live('mouseenter', onFeatureSelected).live('mouseleave',onFeatureUnselected);
function onFeatureSelected(event) {
// I stuff the lookup attribute (I'm lazy) into a global
// a global, because there can be only one
hoveredItem = $(this).attr('lookup');
/* Do something here to indicate the onhover */
// find the layer pagination id
var feature = findFeatureById(hoveredItem);
if (feature) {
// use the pagination id to find the event, and then trigger the click for that event to show the popup
// also, pass a null event, since we don't necessarily have one.
feature.marker.events.listeners.click[0].func.call(feature, event)
}
}
function onFeatureUnselected(event) {
/* Do something here to indicate the onhover */
// find the layer pagination id
var feature = findFeatureById(hoveredItem);
if (feature) {
// use the pagination id to find the event, and then trigger the click for that event to show the popup
// also, pass a null event, since we don't necessarily have one.
feature.marker.events.listeners.click[0].func.call(feature, event)
}
/* Do something here to stop the indication of the onhover */
hoveredItem = null;
}
function findFeatureById(featureId) {
for (var key in map.layers) {
var layer = map.layers[key];
if (layer.hasOwnProperty('features')) {
for (var key1 in layer.features) {
var feature = layer.features[key1];
if (feature.hasOwnProperty('id') && feature.id == featureId) {
return feature;
}
}
}
}
return null;
}
also note that I keep map as a global so I don't have to reacquire it everytime I want to use it