I'm trying to parse and display a geojson representation of the UK postcode districts on a Here Map using the JavaScript API within a VueJs application.
The code is relatively simple - districtGeojson is the JSON document. The following function is called after the map has been initialised and displayed:
processGeojson() {
const reader = new H.data.geojson.Reader(districtGeojson, {
disableLegacyMode: true
});
reader.parse();
const layer = reader.getLayer();
this.shapes = layer;
console.log(layer.isValid(7));
try {
this.map.addLayer(layer);
} catch (err) {
console.log('err adding layer', err);
}
}
As you can see, there's a console.log() in there to do some kind of checking on the layer's validity at the default zoom level and it returns true.
All I see is the map flicker briefly and then the plain map is shown. Is there a way to get some feedback from the API on what is going wrong, it seems to just fail silently - addLayer throws no exception?
If necessary, I can share the JSON document but as it's large (5Mb) I wanted to see if there was anything obviously wrong with this code first.
So, it appears there may be an error in the documentation here: https://developer.here.com/documentation/maps/3.1.17.0/api_reference/H.data.geojson.Reader.html#getLayer
which states:
var reader = new H.data.geojson.Reader('/path/to/geojson/file.json');
reader.parse();
// Assumption: map already exists
map.addLayer(reader.getLayer());
Whereas what just worked for me was to call parseData() rather than parse(), passing the source data. I can do:
var reader = new H.data.geojson.Reader('');
reader.parseData(districtGeojson);
// Assumption: map already exists
map.addLayer(reader.getLayer());
But not
var reader = new H.data.geojson.Reader('./Districts.json');
reader.parse();
// Assumption: map already exists
map.addLayer(reader.getLayer());
Which begs the question of why the file path is required in the constructor as I cannot get the reader to parse data that is passed as a path in the constructor.
Related
Background:
I'm currently integrating HERE maps into our web-based application. I'm trying both - HERE provided Javascript API and Leaflet at the same time to find the best approach for our use-case.
While JavaScript API provided by HERE maps is OK, rendering wise Leaflet performs much better when using raster tiles.
Issue:
It would be fine by me to use raster tiles + leaflet, but our application also needs to display traffic incidents data.
Traffic incident data is provided by HERE in JSON and XML formats (Documentation link, Example JSON). They provide [Z]/[X]/[Y], quadkey, prox, bbox, or corridor filters which can be used to retrieve filtered data set.
I've tried using [Z]/[X]/[Y] addressing with custom L.TileLayer implementation which loads appropriate JSON, converts it to GeoJSON and displays GeoJSON on map. However that approach is very inefficient and significant performance drop is visible.
Question:
Maybe anyone has already solved this issue and could share any insights on how the HERE traffic incidents could be shown on Leaflet map without encountering performance issues?
I created the following script, which works without any performance issues:
var fg = L.featureGroup().addTo(map);
function loadTraffic(data) {
fg.clearLayers();
var d = data.TRAFFICITEMS.TRAFFICITEM.map((r) => {
var latlngs = [];
if (r.LOCATION.GEOLOC) {
if (r.LOCATION.GEOLOC.ORIGIN) {
latlngs.push(L.latLng(r.LOCATION.GEOLOC.ORIGIN.LATITUDE, r.LOCATION.GEOLOC.ORIGIN.LONGITUDE));
}
if (r.LOCATION.GEOLOC.TO) {
if (L.Util.isArray(r.LOCATION.GEOLOC.TO)) {
r.LOCATION.GEOLOC.TO.forEach((latlng) => {
latlngs.push(L.latLng(latlng.LATITUDE, latlng.LONGITUDE));
})
} else {
latlngs.push(L.latLng(r.LOCATION.GEOLOC.TO.LATITUDE, r.LOCATION.GEOLOC.TO.LONGITUDE));
}
}
}
var desc = r.TRAFFICITEMDESCRIPTION.find(x => x.TYPE === "short_desc").content;
return {
latlngs,
desc
}
})
console.log(d);
d.forEach((road)=>{
L.polyline(road.latlngs,{color: 'red'}).addTo(fg).bindPopup(road.desc);
});
map.fitBounds(fg.getBounds())
}
If this script is not working for you, please share your json file.
Ok, so I've found a solution for this task. Apparently I was on a good path, I only needed to optimize my implementation.
What I had to do to achieve appropriate performance is:
Create custom CircleMarker extension which would draw custom icon on canvas
Create JS worker which would fetch the data from a given URL, transform it to GeoJSON and return GeoJSON to it's listener
Create custom GridLayer implementation, which, in fetchTile function, creates worker instance, passes it a link with appropriate [Z]/[X]/[Y] coordinates already set, adds listener, which listens for worker's done event and returns empty tile
On worker's done event, custom GridLayer implementation creates GeoJSON layer, adds it to the dictionary with coordinates as a key and, if zoom level is still the same - adds that layer to the map
Add zoomend observer on a map, which removes any layers that does not match current zoom level from the map
Now the map is definitely usable and works way faster than original HERE JS API.
P.S. Sorry, but I can't share the implementation itself due to our company policies.
I am looking to implement my Keras model into my website with the Keras.js library. One problem with this library is that when inputting data in javascript, only a Float32Array() is allowed as an input. This type of array is 1D, and in my Keras model, the input is 3D. I have asked on the Keras.js issues page and found some potential solutions such as adding an embedding layer, but that requires a specific input shape, but I would like to have any 3D input work, as it did when I trained the model. The model structure is simple, there is an input layer which takes an array of dimension mxnx3 (it is an image of unknown size with r, g, and b channels) and a Conv2D layer, which then outputs an mxnx1 array. I know the model works because it can give a good output based on an input, so the only problem I have is with the transition to Keras.js. Here is the JS code that I have at the moment.
function predictImageWithCNN(data) { //'data' is mxnx3 array
var final = [];
//instantiating model from json and buf files
var model = new KerasJS.Model({
filepaths: {
model: 'dist/model.json',
weights: 'dist/model_weights.buf',
metadata: 'dist/model_metadata.json'
},
gpu: true //MAY NEED TO CHANGE (NOT SURE REALLY)
});
//Ready the model.
model.ready()
.then(function() {
//This matches our input data with the input key (b/c Sequential Model)
var inputData = {
'input_1': new Float32Array(data)
};
// make predictions based on inputData
return model.predict(inputData);
})
.then(function(outputData) {
//Here we take the outputData and parse it to get a result.
var out = outputData['output']; //Gets output data
console.log(out);
//TODO: Put in code to assign outputData to 'final' so we can then convert it
// This should not be too hard to do.
})
.catch(function(err) {
console.log(err);
// handle error
});
return final; // should return nxmx1 array of vals 0-1.
}
If anyone had any suggestions for how to resolve this, that would be very much appreciated. Thanks! :)
I had the same problem with an LSTM. The way I got around this was by training using a flattened version of the data but using a reshape layer as the first layer to get it to the shape I needed for my LSTM. eg.
model = Sequential()
model.add(Reshape((40,59),input_shape=(1,2360)))
model.add(LSTM(128, input_shape=(maxlen, len(chars))))
model.add(Dense(len(chars)))
model.add(Activation('softmax'))
optimizer = RMSprop(lr=0.01)
model.compile(loss='categorical_crossentropy', optimizer=optimizer)
Then in Keras.JS I can feed in the flattened version from Float32Array
I have a hashtable I am trying to use in an vector layer so this is what I made:
var make = [], remove = [];
for (var key in data) {
var val = data[key];
if (featureCache[key]) {
if (val._ts > featureCache[key]._ts) {
var geom = featureCache[key].getGeometry();
The problem starts here. From what I have found out so far I have two options:
geom.setCoordinates(latlng([val.Latitude, val.Longitude]));
geom.flatCoordinates = latlng([val.Latitude, val.Longitude]);
However the first one bubbles up and makes the map render, this is a problem because I have over 1500 features and I plan on using more.
The second line gives me assertion failed, and I dont know how to debug it.
featureCache[key]._ts = val._ts;
}
} else {
make.push(featureCache[key] =
new ol.Feature({
geometry: new ol.geom.Point(latlng([val.Latitude,val.Longitude]))
})
);
featureCache[key]._ts = val._ts;
}
}
source.addFeatures(make);
This needs to be run every other second or so because I want the data to be as realtime as possible.
And if this is a really stupid way do do it, I would like to know that too.
I am not sure how you could do it with the new API, but you could try this:
clone an old geometry and update it, do this for each
add the clone to a collection that OL is not aware of (avoids the map render / updates)
once that collection is ready, remove the old collection from OL and set the new (batch)
Doing this for several thousand items should work pretty fast on 'modern' browsers as you set it only once. The key point is to avoid triggering updates while you update each entry.
Maybe there are ways to avoid the bubbling/events to allow for batch updates (no triggering of map render for each change) ?
I have been working on an android application featuring google maps, I am requesting for coordinates to a node js server and the response is then parsed to double type and when passed to an object of Marker the marker appears on an inaccurate location, I have copied the coordinates from the database and when I pass them direct to the Marker's object, the marker appears on the right location, I also have an alert box displaying the value of the markers as I receive them from the serve and it seems to be displaying the right coordinates, what I think is there is some garbage value with in the variables to which I assign the coordinates or it can be an issue in parsing while i get them in the form of JSON from the server and parse them to double
I have done a little search but I couldn't find a solution specifically addressing my issue, if I haven't been thorough I am sorry, thank you all in advance for your help.
Here is the code:
The code when I receive the coordinates and parse them (via socketIO):
public void on(String functionName, IOAcknowledge ioAck, Object... arguments) {
if("points_of_interest".equals(functionName)){
temp=arguments[0].toString();
try {
JSONObject object=new JSONObject(temp);
String idStop=object.getString("route_id");
String longt=object.getString("stop_longg");
String lat=object.getString("stop_lat");
lati=Double.parseDouble(lat);
lngi=Double.parseDouble(longt);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Now here is the function call that creates the markers in the onCreate method:
map=((MapFragment) getFragmentManager().findFragmentById(R.id.allBusMaps)).getMap();
createStops();
Here is the code for createStops() method:
public void createStops(){
map.clear();
// LatLng lhr=new LatLng(31.500794,74.316777);
LatLng lhr=new LatLng(lati,lngi);
Marker lahore = map.addMarker(new MarkerOptions().position(lhr).title("busStop"));
map.moveCamera(CameraUpdateFactory.newLatLngZoom(lhr, 15));
}
In the above mentioned case case when I am passing the value through variables this is the output being generated:
But when I pass the values direct to the Marker's object the marker appears on the right locations:
e.g. LatLng lhr=new LatLng(31.500794,74.316777);
I have found the solution to the above problem i was having on repeated debugging,
the problem i noticed was that lati and lngi objects were not getting their respective values for a specified time interval, until which both of these objects remained to an initial value of 0.0 and 0.0 respectively hence on majority of test attempts the marker was not appearing at the accurate location.
To overcome this problem, i have enforced a wait of 500 milliseconds before i call the createStops method, that gives the lati and lngi objects sufficient time to attain their respective values from the server.
Here is what worked for me:
map=((MapFragment) getFragmentManager().findFragmentById(R.id.allBusMaps)).getMap();
//new pointMarker().execute();
try {
Thread.sleep(500); //a wait of 500 milliseconds
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
createStops(); // this is the method that creates the markers
Thank you everyone for your help.
I'm running the following script that does geocoding:
function find_callback(response){
var map = g_waze_map.map;
var first_result = response[0];
var lonlat = new OpenLayers.LonLat(first_result.location.lon,first_result.location.lat);
g_waze_map.map.setCenter(lonlat);
var size = new OpenLayers.Size(36,47);
var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
var icon = new OpenLayers.Icon('http://www.waze.co.il/images/home.png',size,offset);
var markers = new OpenLayers.Layer.Markers( "Markers" );
map.addLayer(markers);
markers.addMarker(new OpenLayers.Marker(lonlat,icon));
map.addPopup(new OpenLayers.Popup.FramedCloud("test",lonlat,null,
"<div style='font-family:Arial,sans-serif;font-size:0.8em;'>"+first_result.name+"<div>",
anchor=null,true,null));
};
When I invoke the 3 calls to "find" in a batch (as part of my JS) I get a map with only the 3rd piont on it. When I invoke them via chrome console (one by one) I get a map with all 3 point on it. Is it the call_back that holds its environment? If so, how can I overcome this?
function onInit(){
g_waze_map.find('<%#Locations[2]%>','find_callback');
g_waze_map.find('<%#Locations[3]%>','find_callback');
g_waze_map.find('<%#Locations[5]%>','find_callback');
}
This script looks very strange to me. I'm assuming you only have one map object (g_waze_map.map) but for each callback you do g_waze_map.map.setCenter(lonlat); where lonlat is the latitude longitude of the first result. That doesn't make any sense.
If you calls to g_waze_map.find are asynchronous then you don't know what order they will finish in so I have no idea why you'd want to setCenter.
Also, why not just:
g_waze_map.find('<%#Locations[2]%>', find_callback);
g_waze_map.find('<%#Locations[3]%>', find_callback);
g_waze_map.find('<%#Locations[5]%>', find_callback);
// without the quotes, find_callback is a function afterall
My work-around was: sending an array of location to the g_waze_map.find();
That achived my goal, but I yet don't understand why it didn't work in the original way.