I'm developing an application, part of which uses OpenLayers (calling a Geoserver-served WMS) displaying some frequently updated data (a vessel track - or more specifically, a series of points).
I would like to have this vessel track updated at a set interval - OpenLayers.Strategy.Refresh seems like the most approparite way to do this. I modified the wms.html example (OpenLayers 2.11) slightly to try this, ie:
underway = new OpenLayers.Layer.WMS("Underway Data",
"http://ubuntu-geospatial-server:8080/geoserver/underway/wms",
{'layers': 'underway:ss2011_v03', transparent: true, format: 'image/gif'},
{isBaseLayer: false},
{strategies : [new OpenLayers.Strategy.Refresh({interval: 6000})]}
);
map.addLayers([layer, underway]);
From what I can tell, this should work as-is (ie refresh the underway layer every 6 seconds), however nothing happens. The underlying WMS is getting updated - if I refresh the map manually, the updated data will appear.
I'm sure I'm missing something fairly obvious, any help would be much appreciated. I'm not getting any errors in Firebug or anything, it's just not doing anything.
Well, it turns out that you can't do a refresh strategy on a WMS service, as far as I can tell. So I converted my code to use WFS instead, and it works as expected. The code:
underway = new OpenLayers.Layer.Vector("WFS", {
strategies: [new OpenLayers.Strategy.BBOX(), new OpenLayers.Strategy.Refresh({interval: 4000, force: true})],
protocol: new OpenLayers.Protocol.WFS({
url: "http://ubuntu-geospatial-server:8080/geoserver/wfs",
featureType: "ss2011_v03",
featureNS: "http://csiro.au/underway",
geometryName: "position"
});
Note that I also need a BBOX strategy. Another gotcha that I found was that I needed to manually specify the geometryName, otherwise it would default to "the_geom", which doesn't exist for my layer.
I'm pretty sure you need to add a new OpenLayers.Strategy.Static() strategy for it to work.
And you need to activate your Refresh strategy which means you have to stick it in a separate variable.
Related
I have an application displaying a EPSG:3414 map running on Openlayers - jsfiddle working sample that works.
I am trying to implement the same on Leaflet - jsfiddle non-working, it is not working as I may have left out something or may have gotten some concept wrong.
Unfortunately, I am quite unfamiliar and am in the process of figuring out how to get this to work.
Any help would be welcome. Thanks!
Updates:
map is ESRI type using ZYX and SVY21 projection):
updated code here (JS fiddle not updated)
the map X-Y does not start at 0/0, they are some other numbers, so it is getting more complicated, I have to do some magic number offset for each level, this works somewhat but is not ideal as the locations are not accurate, close but not accurate enough.
Leaflet Sample:
var proj = new L.Proj.CRS.TMS('EPSG:3414', "+proj=tmerc +lat_0=1.366666666666667 +lon_0=103.8333333333333 +k=1 +x_0=28001.642 +y_0=38744.572 +ellps=WGS84 +units=m +no_defs",
[4257.9355, 11470.3186, 64359.3062, 49067.5413],
{
resolutions:[76.4372195411057,38.2186097705529,19.1093048852764,9.55465244263822,4.77732622131911,2.38866311065955,1.19433155532978,0.597165777664889,0.298450596901194]
}
);
var map = L.map('my_map', {
crs: proj,
continuousWorld: true,
worldCopyJump: false
});
map.addLayer(new L.Proj.TileLayer.TMS('http://{s}.onemap.sg/ArcGIS/rest/services/basemap/MapServer/tile/{z}/{y}/{x}', proj, {
subdomains: ['t1', 't2'],
tms: true,
continuousWorld: true
}));
Does this example
http://esri.github.io/esri-leaflet/examples/non-mercator-projection.html
or this example help
http://blog.thematicmapping.org/2012/07/using-custom-projections-with-tilecache.html
I would use firebug in firefox with the example and then look to see if there are error messages in the console.
Also I would try it on a straight web page before I put it in JS Fiddle. Just one less thing that might be interfering.
I am using the heatmap.js library from pa7 in github successfully to make some heatmap
http://www.patrick-wied.at/static/heatmapjs/example-heatmap-leaflet.html
But when I updated the heatmap with additional data or new data points, the leaflet library does not really update with the additional data points. There seems to be no method that can used for redraw. I dont know if someone has already solved this problem, thats why I am asking to see. Here is the simple portion of my additional script:
ndata=[{lat: 13.59, lon:-17.05, value: 11},{lat: 33.08, lon:-103.24, value: 19}]
testData.data.push.apply(ndata)
layer.addData(testData.data);
There is no errors in the console of Google chrome or no exceptions.
Thanks
There is a redraw method in the layer:
var heatmapLayer = L.TileLayer.heatMap();
// add layer to map
heatmapLayer.redraw()
Having said this, in the latest version, the heatmap layer automatically redraws when you set the data (see https://github.com/pa7/heatmap.js/blob/d09c4e99852b4849e2b4d4f12976a9fce0327ca5/src/heatmap-leaflet.js#L112).
According to the OpenLayers documentation, the constructor, OpenLayers.Map(), allows for an additional property extent which is "The initial extent of the map" (see here).
However, I cannot get it to have any effect. I know I can set an initial extent by calling .zoomToExtent() after constructing the map. But I would like to use this extent property because I set a zoomend event in the eventListeners property but don't want it to trigger with an initial call to .zoomToExtent(). Does anyone have a clue how to use this extent property?
This is the code that isn't working
map = new OpenLayers.Map('map',{
extent: bounds,
layers: [osmLayer,vectorLayer],
projection: "EPSG:900913",
eventListeners: {
zoomend: function() {
//..zoomend event listener code
}
}
});
In the above example:
bounds is a valid OpenLayers.Bounds object
osmLayer and vectorLayer are valid OpenLayers.Layer objects of which osmLayer is the base layer.
What happens with above code is that the map is completely zoomed out (in fact you can't actually see anything) and any attempts to pan results in errors being thrown. To get to map into a correct state the user has to zoom in and then panning works again and you can see the map.
I'm afraid I'm not sure off-handedly how to make the code you listed work, however the following alternative has worked for me:
map = new OpenLayers.Map('map',{projection: new OpenLayers.Projection("EPSG:4326")});
//ADD A BUNCH OF LAYERS AND SUCH HERE
//Set map center to this location at this zoom level
map.setCenter(new OpenLayers.LonLat(-93.9196,45.7326),5);
As a last resort, you could put a line at the beginning of your zoom function:
if(!map_ready) return;
And then set the variable map_ready to true at the appropriate time.
I ran into the same problem.
The "extent" option mentioned in the documentation is there probably by mistake. It does not exist. I checked the source code and could not see any code handling such option. I contacted the author and he already created a pull request for the removal of this option from the documentation.
I am trying to use the Google Maps V3 API ClusterManager from http://cm.qfox.nl/ to cluster together markers on a map, but I'm hitting the same error in my code as in the original web site - an error when the page is loaded the first time, or reloaded:
Uncaught TypeError: Cannot call method 'fromLatLngToPoint' of undefined ClusterManager_v3.js:586
ClusterManager.latlngToPoint ClusterManager_v3.js:586
ClusterManager._getMarkerBounds ClusterManager_v3.js:645
ClusterManager._cacheMarkerIcon ClusterManager_v3.js:580
ClusterManager.update ClusterManager_v3.js:345
(anonymous function) ClusterManager_v3.js:91
It works fine after the initial load, so I'm fairly sure it's because of a timing issue - e.g., the map or marker isn't initialized before it being used. Unfortunately, I can't figure out a way to wait for everything to initialize because Javascript isn't my first language. Any help or pointers would be most welcome. The source from the web site is the code I'm using almost exactly.
UPDATE:
If found that changing the line:
cm._requestUpdate(50);
to
cm._requestUpdate(250);
Prevented the error. Changing it to 150 resulted in the error occurring 3/10 times. I'm not entirely sure this is a fix, but it maybe so I'm leaving this posted just in case anyone else has a better solution or wants to know mine.
For using Projection methods , it must be initialized. Map will trigger "projection_changed" event, when Projection will be created.Only after that you can use map.getProjection(). So my suggestion is, to add "projection_changed" event's listener, and initialize ClusterManager when it is called:
google.maps.event.addListenerOnce(map, 'projection_changed', function(){
var cm = window.cm = new ClusterManager(
map,
{
objClusterIcon: new google.maps.MarkerImage('markers/cluster.png', false, false, false, new google.maps.Size(20,20)),
objClusterImageSize: new google.maps.Size(20,20)
}
);
// now json contains a list of marker positions. lets add them.
for (var i = 0; i < json.length; ++i) {
.....
}
cm._requestUpdate(50);
});
I need good examples and best practices on program architecture.
I'm trying to build a JS UI for an app that works with Google.Maps. In the 1st draft, user should be able to draw geometric shapes on the map in a way similar to G.M. Then the shapes are sent through AJAX and the response is displayed.
Problem is that the code got complicated just with polygons editing.
Inspired by Joel's "Duct-tape Programmer", I tried to sketch a straightforward code that makes actions and switches event handlers, to avoid big if-else trees. "New poly" button creates an observer for map.onclick, changes event handlers for other buttons or hides them, and hides itself, etc.
The downside of this approach is that data handling code is mixed with interface. A code that creates a div container to display the data on the new polygon stands next to the code that deals w/ G.M or w/ the shape data. If I want to revise the UI, I'll need to process the WHOLE app.
I could review it later and move this UI-generating code elsewhere, but then came my lead programmer. He instead insisted on "messaging" approach: a simple event system where objects subscribe to events and fire them. Interface code can be perfectly isolated from data handling and talking to G.M, but now each listener has to double-check if this message is to it.
For example, clicking on a poly on a map must highlight it and start editing. But not if another poly is being drawn. So, some are-you-talking-to-ME?-code is necessary everywhere.
I'll appreciate good examples of UI architecture approaches.
The event handling idea suggested to you is a good approach.
Here's some more ideas:
Make the shape drawing thing a component
The shape drawing component sends events to other code, to react to stuff like "editing" or "clicked"
This component could also handle the editing part - It sends "clicked" event to controller, controllers tells the component to go into edit mode
While in edit mode the component would not send normal "clicked" events until the editing was closed, avoiding your problem of having to check
In general, it's a good idea to have a "view" layer which simply deals with displaying the data and sending events about user actions on that data (ie. clicks, etc.) to a "controller" layer, which then decides what to do - for example it could decide to change the view into editing mode.
I don't know if this is beside the point. But I use this as a temple for all my javascript projects.
(function () {
var window = this,
$ = jQuery,
controller,
view,
model;
controller = {
addEventForMenu : function () {
// Add event function for menu
}
};
view = {
content : {
doStuff : function () {
}
},
menu : {
openMenuItem : function () {
}
}
};
model = {
data : {
makeJson : function () {
// make json of string
},
doAjax : function () {
},
handleResponse : function () {
}
}
}
$.extend(true, $.view, view);
})();
The good thing here is that it's only the view object that is extended to the DOM, the rest is kept inside the anonymous scope.
Also in bug project i create on of these files for each part ie, map.js, content.js, editor.js
If you just mind the naming of your methods in the view object you can have as many files as you like during development. When the project is set in to a production environment I just make it one file and minify it.
..fredrik
In short publisher-subscriber paradigm works well to make geometric shapes. First make command-line which primitive is base polygon publisher publishes. Canvas object seems obvious here to paint, usual method repaint() for updating the client view (eventdriven programming normally in C you can review eg opengl or glut eventdriven graphics), combined with the so-so gmap API I too use, publisher-subscriber pattern or factory are good design patterns whatever graphics implementation. tricky gmaps specific thing is latitude and longitude switch places in the array between json and persistence layer, there's no serverside reverse geocoding yet, naming is kinda inconsistent, and for multilingua apps names both change relative user human language and are doubled (Paris in Text, Paris in France...),. Look if you like my going implementation, registers geographic names and spatial coordinated relative user with geoip worldwide
function wAdd(response){
map.clearOverlays();
if(!response||response.Status.code!=200){
}
else{
try{
place=response.Placemark[0];
point=new GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
marker=new GMarker(point);
map.addOverlay(marker);
marker.openInfoWindowHtml('<a href="/li?lat='+place.Point.coordinates[1]+'&lon='+place.Point.coordinates[0]+'&cc='+place.AddressDetails.Country.CountryNameCode+'">'+place.AddressDetails.Country.AdministrativeArea.Locality.LocalityName+'<span id="wr2"></span> '+ nads( place.Point.coordinates[1],place.Point.coordinates[0] )+' ' +'<img src="http://geoip.wtanaka.com/flag/'+place.AddressDetails.Country.CountryNameCode.toLowerCase()+'.gif">');
}
catch(e){
try{
place=response.Placemark[0];
point=new GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
marker=new GMarker(point);
map.addOverlay(marker);
marker.openInfoWindowHtml('<a href="/li?lat='+place.Point.coordinates[1]+'&lon='+place.Point.coordinates[0]+'&cc='+place.AddressDetails.Country.CountryNameCode+'">'+place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName+'<span id="wr2"></span> '+ nads( place.Point.coordinates[1],place.Point.coordinates[0] )+' ' +'<img src="http://geoip.wtanaka.com/flag/'+place.AddressDetails.Country.CountryNameCode.toLowerCase()+'.gif">');
}
catch(e){
try {
place=response.Placemark[0];
point=new GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
marker=new GMarker(point);
map.addOverlay(marker);
marker.openInfoWindowHtml('<a href="/li?lat='+place.Point.coordinates[1]+'&lon='+place.Point.coordinates[0]+'&cc='+place.AddressDetails.Country.CountryNameCode+'">'+place.AddressDetails.Country.CountryName+'<span id="wr2"></span> '+ nads( place.Point.coordinates[1],place.Point.coordinates[0] )+' ' +'<img src="http://geoip.wtanaka.com/flag/'+place.AddressDetails.Country.CountryNameCode.toLowerCase()+'.gif">');
}
catch(e){
place=response.Placemark[0];
marker=new GMarker(point);
map.addOverlay(marker);
marker.openInfoWindowHtml(''+place.address+'');
}
} }
}map.addOverlay(geoXml);
}
i would recommend having few object variables containing the state (0, drawing, editing, ... any other required) - this would help you deciding whether to allow event handling or just bury it if for example drawing is done and clicking on editable polygone happens.
as for the UI - I am not sure if your question is aimed at you - developing the script or at the user as you are mixing two things here.
keep in mind that for a user everything should be as simple as possible: if he is editing, don't allow him to draw. if he is drawing, don't allow him to edit (overlapping of polygons could occur). however - allow user quickly to switch from editing (e.g. right click?) to drawing - or in other words cancel the current state.
The first thing I would do is create a service that wraps over the google API. This is so that later if you need to change mapping services (windows maps or yahoo maps). Then you can put a facade over the google service. Then you might want to put some wrappers over your service and split it up into a view(output) and model(input) and manage this with controllers/presenters. Check into Model View Controller / Model View Presenter / Presenter First / Humble Dialog on Wikipedia. It should discuss the seperation that your looking for. Also Martin Fowler web page goes into presentation patterns. You should check out my old blog ugly-lisp-code. I have references to event driven/event messaging.
If you have a one-to-one pub/sub just store an event-handler(closure/lambda/first-order-function) in the object that is going to fire the event.
If you have a one-to-many pub/sub then you will need a more complex object to store your closures.
LOL! Right now I've been looking at this same exact issue. I'm going to be writing about using a presenter-first in JavaScript on my blog. A bare bones start on presenter and model.
[edit] you might want to check out this stackoverflow question. One of the answer has a link to separating concerns into MVC. The link is on A List Apart.