So I'm working on a webpage using the Google Earth API where a shuttle moves around the town and picks up passengers. PASSENGERS is a predefined array of objects and is a separate javascript file due to the length. Here is my code for the function populate which populates the 3D map with placemarks:
function populate()
{
// mark houses
for (var house in HOUSES)
{
// plant house on map
new google.maps.Marker({
icon: "https://google-maps-icons.googlecode.com/files/home.png",
map: map,
position: new google.maps.LatLng(HOUSES[house].lat, HOUSES[house].lng),
title: house
});
}
// get current URL, sans any filename
var url = window.location.href.substring(0, (window.location.href.lastIndexOf("/")) + 1);
// scatter passengers
for (var i = 0; i < PASSENGERS.length; i++)
{
// pick a random building
var building = BUILDINGS[Math.floor(Math.random() * BUILDINGS.length)];
// prepare placemark
var placemark = earth.createPlacemark("");
placemark.setName(PASSENGERS[i].name + " to " + PASSENGERS[i].house);
// prepare icon
var icon = earth.createIcon("");
icon.setHref(url + "/img/" + PASSENGERS[i].username + ".jpg");
// prepare style
var style = earth.createStyle("");
style.getIconStyle().setIcon(icon);
style.getIconStyle().setScale(4.0);
// prepare stylemap
var styleMap = earth.createStyleMap("");
styleMap.setNormalStyle(style);
styleMap.setHighlightStyle(style);
// associate stylemap with placemark
placemark.setStyleSelector(styleMap);
// prepare point
var point = earth.createPoint("");
point.setAltitudeMode(earth.ALTITUDE_RELATIVE_TO_GROUND);
point.setLatitude(building.lat);
point.setLongitude(building.lng);
point.setAltitude(0.0);
// associate placemark with point
placemark.setGeometry(point);
// add placemark to Earth
earth.getFeatures().appendChild(placemark);
// add marker to map
var marker = new google.maps.Marker({
icon: "https://maps.gstatic.com/intl/en_us/mapfiles/ms/micons/man.png",
map: map,
position: new google.maps.LatLng(building.lat, building.lng),
title: PASSENGERS[i].name + " at " + building.name
});
//remember passenger's placemark and marker for pick-up's sake
PASSENGERS[i].lat = placemark.getGeometry.getLatitude();
PASSENGERS[i].lng = placemark.getGeometry.getLongtitude();
}
}
However, when I load my page I get the following error from the console: Uncaught TypeError: undefined is not a function regarding the two last lines of code:
PASSENGERS[i].lat = placemark.getGeometry.getLatitude();
PASSENGERS[i].lng = placemark.getGeometry.getLongtitude()
Any ideas how I can correctly get the lat and lng? Thank you in advance.
As others have said getGeometry is a method and needs brackets () to be executed.
Also looking at your code you can just use building.lat and building.lng - as these are the properties used to set the placemark's geometry in the first place.
Really it is pointless calling more methods, getGeometry().getLatitude(), to get a value you already have.
The person is at the building, no?
e.g.
PASSENGERS[i].lat = building.lat;
PASSENGERS[i].lng = building.lng;
Try
PASSENGERS[i].lat = placemark.getGeometry().getLatitude();
PASSENGERS[i].lng = placemark.getGeometry().getLongtitude();
Related
I know this is a common error but I'm not seeing my specific problem. Doing a little basic JS practice and running into this error only when using a nested array of coordinate pairs. If I only try and map one single location the error goes away. Any thoughts as to why this might be happening? Code below.
var options = {
center: [39.8333,-98.5833],
zoom: 4
}
// instantiate the leaflet object
var map = L.map('map', options);
// Get basemap URL from Leaflet Providers
var basemap_url = 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}'
// Get basemap attributes from Leaflet Providers
var basemap_attributes = {
attribution: 'Tiles © Esri — Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012'
}
// Add tileset as a layer in our map by passing as arguments to tileLayer method
var tiles = L.tileLayer(basemap_url, basemap_attributes);
// add tiles to map with addLayer method
map.addLayer(tiles);
// variable to hold string value of cities
var cities = ["Victoria", "Bettles", "Truth or Consequences"];
var cityCoords = [
[48.430077, -123.353043],
[66.905857, -151.533330],
[33.1284, -107.25283]
];
var cityPops = [92141, 13, 5948];
var cityCapitals = [true, false, false];
// loop through all the elements in the cities array
for(let i = 0; i <= cities.length; i++){
let cityName = cities[i];
let cityPop = cityPops[i];
let cityCap = cityCapitals[i];
let namePop = `<b>${cityName}</b><br> population: <b>${cityPop}</b><br>`;
let popup = `${cityName} population: ${cityPop}`
console.log(popup)
if (cityCap == true) {
popup += 'is a capital city'}
L.marker(cityCoords).addTo(map)
.bindPopup(popup);
}
As commented, replacing
L.marker(cityCoords).addTo(map)
with
L.marker(cityCoords[i]).addTo(map)
solves the problem by passing a LatLng array of numbers to marker as covered in documentation.
I deduce that Leaflet converts an array of arrays to null if the array should have contained numeric coordinate values, and then generates errors trying to access lat or lng properties of what should have been a valid conversion result.
I am loading custom coordinates into my map application via JSON. I have been able to find out how to color code the markers based on feature properties, but one of my next steps will be to create filters to show or hide markers based on the properties.
My code starts like this:
var map;
var infowindow = new google.maps.InfoWindow();
function initialize()
{
var mapCanvas = document.getElementById('map');
var mapOptions = {
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(mapCanvas, mapOptions);
map.data.loadGeoJson('/map_json.php', null, SetBounds);
map.data.setStyle(function(feature) {
var color = 'FF0000';
var symbol = '%E2%80%A2'; // dot
// color selection code here [...]
return /** #type {google.maps.Data.StyleOptions} */ {
icon: 'http://chart.apis.google.com/chart?chst=d_map_pin_letter_withshadow&chld=' + symbol + '|' + color
};
}
I already found how I can access the imported data through a jquery autocomplete search:
$(input).autocomplete({
minLength: 0,
source: function(request, response) {
data = [];
map.data.forEach(function(feature)
{
var str = request.term.toUpperCase();
if (String(feature.getProperty('name')).toUpperCase().indexOf(str) > -1)
{
data.push({id: feature.getProperty('id'), name: feature.getProperty('name'), address: feature.getProperty('address')});
}
});
response(data);
},
select: function(event, ui)
{
map.data.forEach(function(feature)
{
if (feature.getProperty('id') == ui.item.id)
{
var content = GetContent(feature);
infowindow.setContent(content);
infowindow.setPosition(feature.getGeometry().get());
infowindow.setOptions({pixelOffset: new google.maps.Size(0, -34)});
infowindow.open(map);
// zoom in
map.setZoom(15);
map.panTo(feature.getGeometry().get());
return false;
}
});
}
})
.autocomplete().data('uiAutocomplete')._renderItem = function(ul, item)
{
return $('<li>')
.append('<a>' + item.name + ' (ID: ' + item.id + ')<br>' + item.address + '</a>')
.appendTo(ul)
};
So using this same principle to run my filters is not a problem.
The problem is that I have not found a way yet to access the visible markers based on the feature information that I have in map.data.
All the examples I found so far are based on the principle of manually adding markers and storing them in an array to access later, e.g.:
var marker = new google.maps.Marker({
position: myLatLng,
map: map,
title: 'Hello World!'
});
But I don't have that - I load the entire set of data using getGeoJson().
How can I access the marker and manipulate it (e.g. hide it or show it) based on the information I can access using map.data.forEach()?
--- Update ---
Here are more details on the project.
The map will markers that are generated from a list of customers. The customers have different categories and properties, so a typical entry form the GeoJSON string would look like this:
{"type":"Feature","geometry":{"type":"Point","coordinates":[0,0]},"properties":{"name":"Customer 1","id":"1001","address":"1234 Test Street, Test City, TX 12345, USA","category":"vendor","active":1}}
Also on the map is a filter box with checkboxes that are checked by default. Clicking any of them will run the filtering code that should hide or remove the markers that are associated with any customers that match that filter.
So if I disable the checkbox that filters "inactive", then only customers with the property "active":1 will remain on the map. If I disable the checkbox that filters "vendors", then all customers with the category "vendor" will be hidden.
Checking the checkboxes again later will undo the hiding of these entries.
What I have found in my research is a lot of mentioning of markers, but ONLY if they are added manually - not via GeoJSON import.
I can see a few potential solutions to my problem - I could ignore the GeoJSON format and instead import the client list into jQuery manually and parse it from there into markers that then go into an array. But then why use the GeoJSON format at all?
My current solution of using map.data.setStyle() (see comment) seems to work and do the job. But I am curious if there isn't another more direct way.
I figured, the filter function would go through all data (map.data.forEach()) to locate any items that should be hidden based on the filters, and then each item would communicate to its associated marker that the marker needs to be hidden. But it is this association that I have not been able to figure out so far.
When I loop through all features (map.data.forEach()), I have access to the data I uploaded, but not to the markers that were placed as a result of the import.
My question is if there is a direct way to access the marker from the feature.
I hope this is clearer now.
--- Update ---
I created a very simple jsfiddle for it:
http://jsfiddle.net/semmelbroesel/9bv68ngp/
This is the concept I want to achieve, and it works as is. My only question is if there is another way to achieve the same results by directly accessing the placed markers instead of using setStyle() to hide/show them.
You don't need to use forEach, since setStyle does already traverse the Features.
If you declare the styling function as:
map.data.setStyle(function(feature) {
var color = 'FF0000';
var symbol = '%E2%80%A2'; // dot
return /** #type {google.maps.Data.StyleOptions} */ {
visible: feature.getProperty('active'), // this links visibility to feature property
icon: 'http://chart.apis.google.com/chart?chst=d_map_pin_letter_withshadow&chld=' + symbol + '|' + color
};
});
You don't need to call the method again, since the style gets bound to the feature property. Setting the active property to false will propagate to the marker style seamlessly.
After that, you can make your filters like (rough example)
var setFilter = function(property, value) {
map.data.forEach(function(feature) {
feature.setProperty('active', feature.getProperty(property) === value);
});
};
and calling for example setFilter('name','John');
Again, this is a rough example. I'd rather implement the filtering method on the google.maps.Data prototype, but this should point you in the right direction.
I'm using Google Maps to highlight a bunch of countries using Fusion Tables to grab the geometry. You can see an example of this here:
http://jsfiddle.net/4mtyu/689/
var layer = new google.maps.FusionTablesLayer({
query: {
select: locationColumn,
from: tableId,
where: "ISO_2DIGIT IN ('AF','AL','DZ','AD','AO','AG','AR','AM','AU','AT','AZ','BS','BH','BD','BB','BY','BE','BZ','BJ','BT','BO','BA','BW','BR','BN','BG','BF','BI','KH','CM','CA','CV','CF','TD','CL','CN','CO','KM','CG','CD','CR','HR','CU','CY','CZ','DK','DJ','DM','DO','EC','EG','SV','GQ','ER','EE','ET','FJ','FI','FR','GA','GM','GE','DE','GH','GR','GD','GT','GN','GW','GY','HT','HN','HU','IS','IN','ID','CI','IR','IQ','IE','IL')"
},
options : {suppressInfoWindows:true},
styles: [{
polygonOptions: {
fillColor: "#000000",
strokeWeight: "0",
fillOpacity: 0.4
}
}]
});
The problems begin when I try to grab too many items from the table. Google uses a URL with all of the query values to grab the data required and with URL encoding it can grow to be quite large in length.
You can see an example of the URL here if you open up the console and check the URLs being thrown in the errors:
http://jsfiddle.net/4mtyu/690/
The URL it generates in that particular example is 3749 characters, way over the 2048 character limit.
Does anybody have any ideas on a way I could prevent the URL from getting this large but at the same time still be able to select 150+ items?
The easiest solution is to move things client-side:
http://jsfiddle.net/4mtyu/725/
Part 1 :: Initialize the map and fusion tables
You can do this how ever you prefer, just make sure the fusion tables have all countries selected. Example:
function initialize() {
//settings
var myOptions = {
zoom: 2,
center: new google.maps.LatLng(10, 0),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
//get map div
map = new google.maps.Map(document.getElementById('map_div'),
myOptions);
// Initialize padded JSON request
var script = document.createElement('script');
var url = ['https://www.googleapis.com/fusiontables/v1/query?'];
url.push('sql=');
//select all the countries!!
var query = 'SELECT name, kml_4326 FROM ' +
'1foc3xO9DyfSIF6ofvN0kp2bxSfSeKog5FbdWdQ';
var encodedQuery = encodeURIComponent(query);
//generate URL
url.push(encodedQuery);
url.push('&callback=drawMap');//Callback
url.push('&key=AIzaSyAm9yWCV7JPCTHCJut8whOjARd7pwROFDQ');//select all countries
script.src = url.join('');
//Add Script to document
var body = document.getElementsByTagName('body')[0];
body.appendChild(script);
}
Part 2 :: Sort countries and render
(a) Once you have the full list of countries, you have to sort them. A simple indexOf check should do the trick.
(b) After sorting we need turn our countries into LatLon Coordinates, this is done in the constructNewCoordinates function (see below)
(c) Then all that's left is to generate the polygon and add it to our map!
Example:
var countries = [...];
//This is the callback from the above function
function drawMap(data) {
//Get the countries
var rows = data['rows'];
for (var i in rows) {
// (a) //
//If the country matches our filled countries array
if (countries.indexOf(rows[i][0]) !== -1)
var newCoordinates = [];
// (b) //
// Generate geometries and
// Check for multi geometry countries
var geometries = rows[i][1]['geometries'];
if (geometries) {
for (var j in geometries) {
//Calls our render function, returns Polygon Coordinates (see last step);
newCoordinates.push(constructNewCoordinates(geometries[j]));
}
} else {
//Calls our render function, returns Polygon Coordinates (see last step);
newCoordinates = constructNewCoordinates(rows[i][1]['geometry']);
}
// (c) //
//Generate Polygon
var country = new google.maps.Polygon({
paths: newCoordinates,
strokeWeight: 0,
fillColor: '#000000',
fillOpacity: 0.3
});
//add polygon to map
country.setMap(map);
}
}
}
}
Part 3 :: Generating the coordinates
// (b) //
function constructNewCoordinates(polygon) {
var newCoordinates = [];
var coordinates = polygon['coordinates'][0];
for (var i in coordinates) {
newCoordinates.push(
new google.maps.LatLng(coordinates[i][1], coordinates[i][0]));
}
return newCoordinates;
}
Once I have created a map with multiple markers using google maps API, I want to additional selections to highlight a subset of markers displayed. I would like to do this without going back to the server. Preferably I would like to store data in marker or array. I could either substitute with new marker or overlay an image on top of the marker. Can anyone propose example of how to do this - specifically part about adding image or change marker.
Example below...
Here's an example, which assumes that when you load your page you have this data returned from the server in JSON.
data = [{
latitude: 103.2,
longitude: 12.3,
isDiscountOutlet: false
}, {
latitude: 101.2,
longitude: 11.3,
isDiscountOutlet: false
}
]
The basic approach is that we store that data in the browser, and use it to update the appearance of markers when changing a selection.
Part 1: Create a global variable to store our markers in
var storedMarkers;
Part 2: Create a Map using the data from the server
function initialize() {
var mapOptions = {
zoom: 4,
center: new google.maps.LatLng(103, 11)
}
var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
// Add the markers. We are going to store them in a global array too,
// so we can access them later.
for (var i = 0; i < data.length; ++i) {
// Create one marker for each piece of data.
var marker = new google.maps.Marker({
position: new google.maps.LatLng(data[i].latitude, data[i].longitude),
map: map
});
// Store that marker, alongside that data.
var dataToStore = {
markerObject: marker,
dataAssociatedWithMarker: data[i]
};
storedMarkers.push(dataToStore);
}
google.maps.event.addDomListener(window, 'load', initialize);
Part 3: Let's show all discount outlets, and hide all other markers, when someone clicks a button
I'm assuming you have a DOM element (a button) with id 'discount'. I'm also going to cheat and use jQuery :)
$("#discount").click(function () {
for (var i = 0; i < storedMarkers.length; ++i) {
var currentStoredMarker = storedMarkers[i];
// Is this marker a discount outlet?
if (currentStoredMarker.dataAssociatedWithMarker.isDiscountOutlet == true) {
// Let's show it!
currentStoredMarker.markerObject.setVisible(true);
} else {
// Let's hide it!
currentStoredMarker.markerObject.setVisible(false);
}
}
});
Hi there I am Using appcelerator, and I want to integrate a map with an array of markers I am getting from a HTTPRequest...
I am effing lost, totally lost.
This is how the map looks like:
var mapview = Titanium.Map.createView({
mapType: Titanium.Map.STANDARD_TYPE,
region: {latitude:33.74511, longitude:-84.38993,
latitudeDelta:0.01, longitudeDelta:0.01},
animate:true,
regionFit:true,
userLocation:true,
annotations:[mountainView]
});
And I have the example of 1 marker hardcoded ...
var mountainView = Titanium.Map.createAnnotation({
latitude:37.390749,
longitude:-122.081651,
title:"Appcelerator Headquarters",
subtitle:'Mountain View, CA',
pincolor:Titanium.Map.ANNOTATION_RED,
animate:true,
leftButton: '../images/appcelerator_small.png',
myid:1 // CUSTOM ATTRIBUTE THAT IS PASSED INTO EVENT OBJECTS
});
So yo create the marker and in the annotations section you add it to the map, the thing here is that I am getting the markers from this:
var url = "http://myURLwithMyParameters";
var xhr = Ti.Network.createHTTPClient({
onload: function(e) {
// this function is called when data is returned from the server and available for use
// this.responseText holds the raw text return of the message (used for text/JSON)
var result = this.responseText;
var xml = Ti.XML.parseString(result);
var items = xml.documentElement.getElementsByTagName("marker");
var name = xml.documentElement.getElementsByTagName("name");
var value = xml.documentElement.getElementsByTagName("address");
var data = [];
for (var i=0;i<items.length;i++) {
data.push({
name: items.item[i].getElementsByTagName("name")[0].textContent,
address: items.item[i].getElementsByTagName("address")[0].textContent
})
Does any one know how to integrate this?
I think I must build the map in the same function as the markers, but I've tried several options and haven't found ANY example of this in the web.
Any clue would be very appreciated.
Thank you in advance.
If all you have is an address, you'll need to forward geocode those addresses to get lat/long coordinates. Those coords are required to place annotations on the map. Check the docs at forwardGeocoder(). There's an example in the KitchenSink