I've been working with the Google Maps Javascript API for several weeks now. I've been having trouble accessing a property of a geoJSON after it has been added to the map and becomes a feature of the map.
For example, lets say I have this sample geoJSON
var geo = {"type":"FeatureCollection","features":[
{"type":"Feature","id":"country","properties":
{"name":"ExampleCountry"},"geometry": {exampleGeometry}}
]};
Lets say I load the geoJSON and want to access the id property of the feature I have just added. Neither feature.id nor feature.getProperty('id') works in this case. From debugging, I found out that I can access the 'id' property via feature.F. That solution was working fine for several weeks, but for whatever reason, last week it ceased to work. I instead have to use feature.K to access the ID property.
var mapOptions = {
center: { lat: -34.397, lng: 150.644},
zoom: 8
};
map = new google.maps.Map(document.getElementById('map-canvas'), {mapOptions});
map.data.loadGeoJson(geo);
map.data.forEach(function(feature) {
//Used to Work, randomly stopped working last week (feature.F is undefined)
var id = feature.F;
//New Solution
var id = feature.K;
});
This doesn't seem to be a permanent solution. Does anyone have any idea how this could have happened?
The id of a feature is not a "property" in the meaning of geoJSON.
There is a getter-method for the id of a feature, use:
feature.getId()//should return 'country'
When you want to get a property(stored in the properties-member) use e.g.
feature.getProperty('name')//should return 'ExampleCountry'
Related
Basically, what I have is a geolocation data (longtitude and latitudes) of 300.000 locations. I have different attributes attached to the data and it is approx. 32MB. Reading it through js and putting markers on google maps is what I've tried, and It works OK when i put only 25 to 2500 markers on my map, I cant really put all of my locations at once. Eventually I want to be able to filter markers through the attributes etc. The locations are all at one city, so I might use my own map or something.
What I want to ask/learn is do you have any better solutions for this particular situation?
Here is my code.
function initJS() {
var promise = getData();
var locations1;
var locations;
promise.success( (data) => {
locations1 = parseData(data);
locations = locations1.filter(location => {
return location.DuyuruTipi == "16";
});
//initializing google map
map = new google.maps.Map(document.getElementById("map"), {
center: { lat: latitude, lng:longtitude},
zoom: zooming,
});
//creating a marker
const svgMarker = {
// path: "M10.453 14.016l6.563-6.609-1.406-1.406-5.156 5.203-2.063-2.109-1.406 1.406zM12 2.016q2.906 0 4.945 2.039t2.039 4.945q0 1.453-0.727 3.328t-1.758 3.516-2.039 3.070-1.711 2.273l-0.75 0.797q-0.281-0.328-0.75-0.867t-1.688-2.156-2.133-3.141-1.664-3.445-0.75-3.375q0-2.906 2.039-4.945t4.945-2.039z",
url: "./assets/marker.svg",
size: new google.maps.Size(20,20),
scaledSize: new google.maps.Size(20,20),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(10, 10),
};
for (var i = 0; i < locations.length; i++) { //locations.length
// init markers
var marker = new google.maps.Marker({
position: { lat: parseFloat(locations[i]["YCoor"]), lng: parseFloat(locations[i]["XCoor"])},
map: map,
label: locations[i]["DuyuruId"],
icon: svgMarker
});
console.log(locations[i]["DuyuruTipi"]);
marker.duyurutipi = locations[i]["DuyuruTipi"];
marker.kazatipi = locations[i]["KazaTipi"];
marker.vsegid = locations[i]["vSegID"];
markers.push(marker);
}
});
Displaying 300000 points directly on the map is not the correct approach and will not perform well, especially as more datasets get added to your map.
In addition, sending 32MB of data or more to the browser is bad form, even for a web map application. If you try out e.g. Google Maps with the network panel open, you'll see that you'll barely go over a few MB even after quite some time using it.
There are a couple approaches that web mappers take to counter this:
Use a service such as Geoserver or Mapserver to split up the data into "chunks" based on what is in the the map clients (openlayers, in your case, according to your answer) viewport. This is the best choice if you could potentially have lots of layers or basemaps in future, but is a lot of work to setup and configure.
Write your own implementation of the above in your back-end. For points, this is relatively simple. This is the best choice for something quick with just a couple of points layers.
In both cases, you'll need to configure your points layer in OpenLayers to use the "bbox" strategy, which will tell it to call your API url whenever the viewport changes enough for more features to be loaded. You will also need to set the minimum resolution for your layer so that it doesn't load too many features all at once when zoomed out.
Lastly, with Openlayers, you'll want to use a VectorImageLayer with a VectorSource for this layer, which will improve performance a lot while allowing you to query and edit your point data.
The above should help to improve your mapping performance.
Well, I went with the OpenLayers API, I think it is harder to implement stuff from docs but they have example applications for every feature. You might want to try that, way better performance if your only need is to put some markers and visualize data.
Without any knowledge of JS, I was forced to implement a map (OSM via Leaflet) on a webpage. On this map, there should be a marker for the actual address of a person. The address is saved as a string in the database.
I can see a map, can add marker to it, but after that, I'm lost.
I've tested some Leaflet-geocoding-plugins, but i must confess, that they're not simple enough for my actual programmming experience.
Another question was about the same problem, but i didn't understand, how to get the lon/lat from an address with the L.Geosearch-plugin for Leaflet.
Can anyone provide me a example of looking up an address (via OSMN or something else, not google/bing or other api-key-needy provider), converting it to lon/lat and add a marker to it on a map?
First you will have to include the .js files of a geocoder in the head of your HTML code, for my example I have used this one: https://github.com/perliedman/leaflet-control-geocoder. Like this:
<script src="Control.Geocoder.js"></script>
Then you will have to initialize the Geocoder in your .js:
geocoder = new L.Control.Geocoder.Nominatim();
Then you will have to specify the address that you are looking for, you can save it in a variable. For example:
var yourQuery = (Addres of person);
(You can also get the address out of your database, then save it in the variable)
Then you can use the following code to 'geocode' your address into latitude / longitude. This function will return the latitude / longitude of the address. You can save the latitude / longitude in an variable so you can use it later for your marker. Then you only have to add the marker to the map.
geocoder.geocode(yourQuery, function(results) {
latLng= new L.LatLng(results[0].center.lat, results[0].center.lng);
marker = new L.Marker (latLng);
map.addlayer(marker);
});
I made a jfsfiddle that
Has an address set
Looks for the coordinates of that address using geosearch
Creates a marker at the coordinates of that address found by geosearch.
It can be found here: https://jsfiddle.net/Alechan/L6s4nfwg/
The "tricky" part is dealing with the Javascript "Promise" instance returned by geosearch and that the address may be ambigous and more than one coordinate may be returned in that case. Also, be careful because the first position in the Leaflet coordinates corresponds to the latitude and the second to the longitude, which is in reverse of the Geosearch "x" and "y" results.
Geosearch returns a promise because it's an asynchronous call. The alternative would have to be a synchronous call and the browser would have to be freezed until an answer was retrieved. More info about promises from MDM (Mozilla) and Google.
In my example, I create a marker for every result found for the indicated address. However, in this case the address is unambiguous and returns only one result.
Breakdown of code:
<!-- Head, imports of Leaflet CSS and JS, Geosearch JS, etc -->
<div id='map'></div>
<script>
// Initialize map to specified coordinates
var map = L.map( 'map', {
center: [ 51.5, -0.1], // CAREFULL!!! The first position corresponds to the lat (y) and the second to the lon (x)
zoom: 12
});
// Add tiles (streets, etc)
L.tileLayer( 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap',
subdomains: ['a','b','c']
}).addTo( map );
var query_addr = "99 Southwark St, London SE1 0JF, UK";
// Get the provider, in this case the OpenStreetMap (OSM) provider.
const provider = new window.GeoSearch.OpenStreetMapProvider()
// Query for the address
var query_promise = provider.search({ query: query_addr});
// Wait until we have an answer on the Promise
query_promise.then( value => {
for(i=0;i < value.length; i++){
// Success!
var x_coor = value[i].x;
var y_coor = value[i].y;
var label = value[i].label;
// Create a marker for the found coordinates
var marker = L.marker([y_coor,x_coor]).addTo(map) // CAREFULL!!! The first position corresponds to the lat (y) and the second to the lon (x)
// Add a popup to said marker with the address found by geosearch (not the one from the user)
marker.bindPopup("<b>Found location</b><br>"+label).openPopup();
};
}, reason => {
console.log(reason); // Error!
} );
</script>
here is my code in html to generate marker and infowindow(with ruby on rails)
var marker=[]
function initMap() {
var latLng1 = new google.maps.LatLng(1.352083, 103.819836);
var myOptions = {
zoom: 12,
center: latLng1,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById('map_canvas'), myOptions);
for(i=0;i<gon.astatic.length;i++){
var latLng = new google.maps.LatLng(gon.astatic[i][1], gon.astatic[i][2]);
if(i<2){
marker[i] = new MarkerWithLabel({position: latLng, map: map,icon:"/assets/green_MarkerV.png" ,labelClass: "labels",labelContent: gon.astatic[i][3]});}
else
{
marker[i] = new MarkerWithLabel({position: latLng, map: map,icon:"/assets/green_MarkerN.png" ,labelClass: "labels",labelContent: gon.astatic[i][3]});
}
var iw =new google.maps.InfoWindow({content: 'HI' });
google.maps.event.addListener(marker[i],"mouseover",function(e){iw.open(map,marker[i]);})
}
this gon is just some 'stupid' method I use to pass data from ruby on rails controller to javascript.
for all marker,the infowindow all appear at corner.
But for my another map(which have only one marker with infowindow)it works fine.
What might be my problem?why this infowindow appear in wrong position?Instead of just above the marker?
EDIT:
After half day's trouble shoot,I feel the problem is at
google.maps.event.addListener(marker[i],"mouseover",function(e){iw.open(map,marker[i]);})
when the listener calls back,the value inside marker is i ,which is not a actual number,so the marker display at a corner.I feel the problem is can't pass variable into addListener,can only put in actual number.How to solve this?
Each instance of the function declared inside the for loop shares the same closure containing the value i, and so all of your addListener calls are essentially calling iw.open(map, undefined) since i will be off the end of the array at the end of the iteration.
See JavaScript closure inside loops – simple practical example for sample solutions to this problem, and How do JavaScript closures work for more information about closures in JavaScript in general.
The problem is with your MarkerWithLabel library. Infowindow take position from marker. Try use this link http://google-maps-utility-library-v3.googlecode.com/svn/tags/markerwithlabel/1.1.8/docs/examples.html . It has all the things that you want to implement. It it's not work then you can also set position for infowindow with setPosition() function just pass latlng that you used to create marker and you are done.
i dont recommend using new gem just to pass data from ruby to js...you can do this simply by many ways...your code seems good but i cannot say how gon is handling your js script.Please take a look at this similar question where i have implemented the same dynamic map with dynamic markers and infowindows.This code is working great
see here
My map redraws seem to be failing because (at the least) I have been dynamically setting the center via
var currCenter = gmap.getCenter();
Then:
var mapOptions = {
center: new google.maps.LatLng(currCenter.ob, currCenter.pb),
zoom: currZoom,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
Appears currCenter.ob is suddenly undefined this morning. It now looks like it's pb & qb instead of ob & pb. I'm in the process of trying to fix the code, is there anything else anyone knows of that was changed?
EDIT: They're undocumented API fields I shouldn't be using, nevermind I fixed it with the info below. Thanks.
I am assuming gmap is a google map object. If that is the case, then getCenter already returns a LatLng object, so creating a new object via
new google.maps.LatLng()
Is somewhat useless, you could simply use currCenter directly.
The problem is that Api Google maps constantly changing this values (ob, .pb) to Latitude and Longitude, you must use lat() and lng() functions to have a stable version
var mapOptions = {
center: new google.maps.LatLng(currCenter.lat(), currCenter.lng()),
zoom: currZoom,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
greetings!
To answer you question. YES. Google Maps API did changed last night. I was doing the same thing you did (calling .ob and .pb), but found that I had to change them to .pb and .qb in my code respectively in order to get the Lat and Long.
Rewrote them as .lng() and .lat() instead of .ob, .pb, .qp. I believe that these members should be more stable if you still want to use unofficial member names.
Is there a way to load a map using js, but show the reference location as an actual places listing?
I'd like to load a map that shows a particular business (I have the places reference number) then display a map around it.
I would just use the iframe, but it doesn't allow for the level of customization that the js library does.
Yes. That is one of the more standard ways of using maps.
You have the lat long for the location to both center your map and drop a marker (probably a custom image of for instance the company logo).
Just follow any of the tutorials. They all show how to do this.
If you want to display a business that you have the reference number for then use the PlacesService.
You will also need to include the places library in the API url.
e.g
// I assume the map is already loaded and defined as variable called map
var placesService = new PlacesService();
placesService.getDetails({reference: placeReferenceNumber}, function(place, status) {
if (status === google.maps.places.PlacesServiceStatus.OK) {
var marker = new google.maps.Marker({
map: map,
position: place.geometry.location
});
// Warning: Viewport may not always be defined
map.setBounds(place.geometry.viewport);
}
});