Auto close InfoWindow Google Maps API when click another marker - javascript

I have problem with my Google Maps API v3 script. I am using CodeIgniter framework.
The problem is when I clicked a marker, and then click another marker, InfoWindow Google Maps on previous marker didn't close. This is my code:
<script type="text/javascript">
var peta;
var gambar_kantor = new Array();
var nama = new Array();
var kategori = new Array();
var alamat = new Array();
var telpon = new Array();
var x = new Array();
var y = new Array();
var i;
var url;
var gambar_marker;
var gambar_kantor;
var baseurl = "<?php echo base_url() ?>";
function map_init() {
var map = new google.maps.LatLng(-6.990411, 110.422542);
var myStyles =[
{
featureType: "poi",
elementType: "labels",
stylers: [
{ visibility: "off" }
]
}
];
var petaoption = {
zoom: 12,
center: map,
mapTypeId: google.maps.MapTypeId.ROADMAP,
styles: myStyles
};
peta = new google.maps.Map(document.getElementById("map_canvas"),petaoption);
getdatabase();
}
function getdatabase(){
var markers = [];
var info= [];
<?php
$query = $this->db->query("SELECT l.id, l.nama, l.gambar, l.alamat, l.telp, l.latittude, l.longitude, k.nama_kategori, k.ikon
FROM lokasi as l, kategori as k
WHERE l.kategori=k.id");
$i = 0;
$js = "";
foreach ($query->result() as $value) {
$js .= 'nama['.$i.'] = "'.$value->nama.'";
alamat['.$i.'] = "'.$value->alamat.'";
telpon['.$i.'] = "'.$value->telp.'";
x['.$i.'] = "'.$value->latittude.'";
y['.$i.'] = "'.$value->longitude.'";
set_icon("'.$value->ikon.'");
var point = new google.maps.LatLng(parseFloat(x['.$i.']),parseFloat(y['.$i.']));
var contentString = "<table>"+
"<tr>"+
"<td align=center><br><b>" + nama['.$i.'] + "</b></td>"+
"</tr>"+
"<tr>"+
"<td align=center width=300px>" + alamat['.$i.'] + "</td>"+
"</tr>"+
"<tr>"+
"<td align=center> Telp: " + telpon['.$i.'] + "</td>"+
"</tr>"+
"</table>";
var currentInfoWindow = null;
var infowindow = new google.maps.InfoWindow({
content: contentString
});
var marker = new google.maps.Marker({
position: point,
map: peta,
icon: gambar_marker,
clickable: true
});
markers.push(marker);
info.push(infowindow);
google.maps.event.addListener(markers['.$i.'], "click", function() {
info['.$i.'].open(peta,markers['.$i.']);
});
';
$i++;
}
// echo JS
echo $js;
?>
}
Any solution to make InfoWindow auto close when another marker clicked?
I have tried several solutions on Stackoverflow and Google but didn't worked. Thanks for your kindly help :)

You could declare just one infowindow, outside of your loop. Then each marker, when clicked, opens that infowindow on itself, filling it with the proper contents.
In the following example I set the contentString property on the marker itself.
var infowindow = new google.maps.InfoWindow();
<?php
foreach ($query->result() as $value) {
....
?>
var marker = new google.maps.Marker({
position: point,
map: peta,
icon: gambar_marker,
clickable: true,
content: contentString
});
markers.push(marker);
google.maps.event.addListener(marker, "click", function() {
infowindow.setContent(marker.content);
infowindow.open(peta,marker);
});
<?php
}
?>

Maybe this link will be of use to you?
Previously answered: Close infowindow when another marker is clicked
Make sure to declare the global variable outside the main google maps code in your JS:
file: var lastOpenedInfoWindow;
Good luck!

Related

how to use marker cluster with php mysql database

i have PHP code with MYSQL database and javascript where i ma using the java script in order to display the Google Map with markers where the code display markers based on their coordination.
i have some markers that have the same coordination but different ID so the map show just the last marker.
what i need is:
either to display all the markers that have the same coordination
or
display one marker with number that indicate the the total number of
the markers that have the same coordination.
code:
<script type="text/javascript">
var map,currentPopup;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
zoom: 8,
center: new google.maps.LatLng(33.888630, 35.495480),
mapTypeId: 'roadmap'
});
var iconBase = 'https://maps.google.com/mapfiles/kml/shapes/';
var icons = {
parking: {
icon: iconBase + 'parking_lot_maps.png'
},
library: {
icon: iconBase + 'library_maps.png'
},
info: {
icon: iconBase + 'info-i_maps.png'
}
};
function addMarker(feature) {
var marker = new google.maps.Marker({
position: feature.position,
//icon: icons[feature.type].icon,
map: map
});
var markerCluster = new MarkerClusterer(map, marker,
{imagePath:
'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'});
var popup = new google.maps.InfoWindow({
content: '<b>Site ID :</b> ' + feature.info +'<br></br>'
+ '<b> Site Name :</b>' + feature.site_name +'<br></br>'
+ '<b>Coordinates :</b> '+ feature.position +'<br></br>'
+ '<b>height :</b> ' + feature.height,
maxWidth: 300
});
google.maps.event.addListener(marker, "click", function() {
if (currentPopup != null) {
currentPopup.close();
currentPopup = null;
}
popup.open(map, marker);
currentPopup = popup;
});
google.maps.event.addListener(popup, "closeclick", function() {
map.panTo(center);
currentPopup = null;
});
}
var features = [
<?php
$prependStr ="";
foreach( $wpdb->get_results("select c.siteID, c.latitude, c.longitude, c.height
, i.siteNAME
FROM site_coordinates2 c LEFT
JOIN site_info i
on c.siteID = i.siteID
where i.siteNAME = '".$site_name."'", OBJECT) as $key => $row) {
$latitude = $row->latitude;
$longitude = $row->longitude;
$siteid = $row->siteID;
$height = $row->height;
$siteName = $row->siteNAME;
echo $prependStr;
?>
{
position: new google.maps.LatLng(<?php echo $latitude; ?>, <?php echo $longitude; ?>),
info:'<?php echo $siteid;?>',
height: '<?php echo $height;?>',
site_name: '<?php echo $siteName;?>',
}
<?php
$prependStr =",";
}
?>
];
for (var i = 0, feature; feature = features[i]; i++) {
addMarker(feature);
}
}
</script>
<?php
//echo "</table>";
}
?>
and i add this library to the code
<script src="https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/markerclusterer.js">
</script>

show route by road between multiple locations on a google map [duplicate]

This question already has an answer here:
Inconsistent behaviour drawing a route between two points in Google Maps v3
(1 answer)
Closed 8 years ago.
I have a code to show the route between multiple locations on a google map.
It is a using a polyline. However, for the return route to the destination it shows 'as the crow flies'.
I am not using a google API key. Do I need to be? How do I use it if I do? Could this be causing the issue?
I've tried the answer from the link suggested and it doesn't work all the time.
I would have thought this would be a fairly common question - how to plot multiple locations using php.
I've been working on it for a while now and I can't find a solution. How do I get it to stop showing the return route as the crow flies but only ON THE ROAD?
The code I have is:
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
var map = null;
var infowindow = new google.maps.InfoWindow();
var bounds = new google.maps.LatLngBounds();
var markers = [
<?php
// loop through the rows of data
if( have_rows('tour_site', $ID) ):
$info_places = array();
$info_all_places = array();
while( have_rows('tour_site', $ID) ) : the_row();
//display a sub field value
$name = get_sub_field('name');
$long = get_sub_field('long');
$lat = get_sub_field('lat');
$info_places = array("name" => $name, "long"=>$long, "lat"=>$lat);
$info_all_places = array($info_places);
foreach ($info_all_places as $info) {
?>
{
"title": <?php echo "'" . $info['name'] . "'"; ?>,
"lat": <?php echo "'" . $info['lat'] . "'"; ?>,
"lng": <?php echo "'" . $info['long'] . "'"; ?>,
},
<?php
}
endwhile;
else :
endif;
?>
{
"title": 'XXX',
"lat": 'XXX',
"lng": 'XXX',
}
];
</script>
<!-- <script src="//www.gmodules.com/ig/ifr?url=http://hosting.gmodules.com/ig/gadgets/file/114281111391296844949/driving-directions.xml&up_fromLocation=xxx&up_myLocations=<?php echo $destination; ?>&up_defaultDirectionsType=&up_autoExpand=&synd=open&w=320&h=55&title=Directions+by+Google+Maps&lang=en&country=US&border=%23ffffff%7C3px%2C1px+solid+%23999999&output=js"></script> -->
<script type="text/javascript">
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(
parseFloat(markers[0].lat),
parseFloat(markers[0].lng)),
zoom:15,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var path = new google.maps.MVCArray();
var service = new google.maps.DirectionsService();
var infoWindow = new google.maps.InfoWindow();
map = new google.maps.Map(document.getElementById("map"), mapOptions);
var poly = new google.maps.Polyline({
map: map,
strokeColor: '#F3443C'
});
var lat_lng = new Array();
var marker = new google.maps.Marker({
position:map.getCenter(),
map:map,
title:markers[0].title
});
bounds.extend(marker.getPosition());
google.maps.event.addListener(marker, "click", function(evt) {
infowindow.setContent(this.get('title'));
infowindow.open(map,marker);
});
for (var i = 0; i < markers.length; i++) {
if ((i + 1) < markers.length) {
var src = new google.maps.LatLng(parseFloat(markers[i].lat),
parseFloat(markers[i].lng));
var smarker = new google.maps.Marker({position:des,
map:map,
title:markers[i].title
});
google.maps.event.addListener(smarker, "click", function(evt) {
infowindow.setContent(this.get('title'));
infowindow.open(map,this);
});
var des = new google.maps.LatLng(parseFloat(markers[i+1].lat),
parseFloat(markers[i+1].lng));
var dmarker = new google.maps.Marker({position:des,
map:map,
title:markers[i+1].title
});
bounds.extend(dmarker.getPosition());
google.maps.event.addListener(dmarker, "click", function(evt) {
infowindow.setContent(this.get('title'));
infowindow.open(map,this);
});
service.route({
origin: src,
destination: des,
travelMode: google.maps.DirectionsTravelMode.DRIVING
}, function (result, status) {
if (status == google.maps.DirectionsStatus.OK) {
for (var i = 0, len = result.routes[0].overview_path.length; i < len; i++) {
path.push(result.routes[0].overview_path[i]);
}
poly.setPath(path);
map.fitBounds(bounds);
}
});
}
}
}
google.maps.event.addDomListener(window,'load',initialize);
</script>
<div id="dvMap" style="width: 600px; height: 450px"> </div>
Check the returned status parameter google.maps.DirectionsStatus.
It might give you an indication for the problem.
If you get OVER_QUERY_LIMIT you should put a delay between the directions service calls or call it only once with an array that contains up to 10 waypoints.
Read more here.

Google Maps Marker Content in box

I get the Coordinates and the Names and "ID" out of an MySQL Table but why is in every Marker the same Text an also the same link ?
Why is the text not different ???
It show only the last ID but it should add after every element in the data base an other marker !
The markers are at the right Position and the "Content" is in the Source Code also right but not at the markes why ?!
Please Help
<script type="text/javascript">
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(51.124213, 10.60936),
zoom: 5,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new
google.maps.Map(document.getElementById("map_canvas"),
mapOptions);
var contentString;
var elementeInArray = 0;
var LatLngList = new Array;
<? php
$i = 1;
foreach($FertigID as $ID) {
$result = mysql_query("SELECT * FROM Daten_CH WHERE Objektnummer = ".$ID.
"");
while ($Ergebnisse = mysql_fetch_array($result)) {
// Werden alle ergebnisse (ID´s) in einen Array geschriebenn
echo $$Ergebnisse["Objektnummer"];
if (isset($Ergebnisse["Gis_y"]) && $Ergebnisse["Gis_y"] != "" &&
$Ergebnisse["Gis_y"] != " ") {
echo $i; ?>
// MARKER TEXT
contentString = '<?php echo $i; ?> </br><?php echo
$Ergebnisse["Objektname"]; ?>
</br><a href="Change.php?ID=<?php echo $ID; ?>">
<input type="button" value="BEARBEITEN"></button></a>';
var Position = new google.maps.LatLng( <? php echo $Ergebnisse["Gis_y"]; ?> , <? php echo $Ergebnisse["Gis_x"]; ?> );
var infowindow = new google.maps.InfoWindow({
content: contentString
});
var marker <? php echo $i; ?> = new google.maps.Marker({
position: Position,
map: map,
title: '<?php echo $Ergebnisse["AA_Objektname"]; ?>'
});
google.maps.event.addListener(marker <? php echo $i; ?> , 'click', function () {
infowindow.open(map, marker <? php echo $i; ?> );
});
LatLngList[elementeInArray] = new google.maps.LatLng( <? php echo $Ergebnisse["Gis_y"]; ?> , <? php echo $Ergebnisse["Gis_x"]; ?> );
elementeInArray++;
<? php
}
}
$i++;
}
?>
// Create a new viewpoint bound
var bounds = new google.maps.LatLngBounds();
// Go through each...
for (var i = 0, LtLgLen = LatLngList.length; i < LtLgLen; i++) {
// And increase the bounds to take this point
bounds.extend(LatLngList[i]);
}
// Fit these bounds to the map
map.fitBounds(bounds);
var opt = {
minZoom: 1,
maxZoom: 12
};
map.setOptions(opt);
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
Because you should assign the contentString to the marker, and update the infowindow with each markers assigned content instead. As it is now, the only contentString available is the last created. Furthermore, you really dont have to create multiple numbered markers. You just need one marker reference only.
var infowindow = new google.maps.InfoWindow();
var marker = new google.maps.Marker({
position: Position,
map: map,
content: contenString, // <-- assign content to the marker itself
title: '<?php echo $Ergebnisse["AA_Objektname"]; ?>'
});
google.maps.event.addListener(marker, 'click', function () {
infoWindow.setContent(this.content);
infowindow.open(map, this);
});
This would do the trick.

Every infowindow is displaying the same data maps api v3

I am really stuck on something. Every map marker's infowindow is displaying the same info. It seems to be the content at the end of an array that i use to store content nodes every time. I am pretty sure it is because the infowindow is not being attached to the proper marker
var markers = [];
var contentArray = [];
var titleArray = [];
var latlngArray = [];
var map;
//var infowindow;
var concert;
function defaultMap()
{
//Latitude: 38
//Longitude: -97
//window.alert("inside function");
var mapOptions = {
center:new google.maps.LatLng(38,-97),
zoom:4,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map"),
mapOptions);
// window.alert("addMarkers the size of contentArray is: "+contentArray.length);
//window.alert("addMarkers the size of the titleArray is: "+titleArray.length);
// window.alert("addMarkers the size of the latLongArray is: "+latlngArray.length);
//for(var i =0;i<2;i++)
//{
// if(i == 0)
// {
// marker = new google.maps.Marker({
// position: new google.maps.LatLng(37.8172784,-96.8909115),
// map:map
// });
// markers.push(marker);
// }
// else
// {
// marker = new google.maps.Marker({
// position: new google.maps.LatLng(37.8172973,-96.8766355),
// map:map
// });
// markers.push(marker);
// }
// //markers[0] = new google.maps.LatLng(37.8172784,-96.8909115);
// //markers[1] = new google.maps.LatLng(37.8172973,-96.8766355);
//
//}
//addMarkers();
}
//function
//
//{
//infowindow = new google.maps.InfoWindow({
//content:list
//});
//google.maps.event.addListener(marker,'click',function(){
// infowindow.open(map,marker);
//});
function addMarkers()
{
//console.dir(contentArray[contentArray.length-1]);
for(var i = 0;i <10;i++)
{
if(i == 0)
{
//window.alert("i = "+i);
console.log(latlngArray[i]);
var marker = new google.maps.Marker({
position:latlngArray[i],
animation:google.maps.Animation.DROP,
icon:'./images/club.png',
title:titleArray[i],
map:map
});
//marker.setMap(map);
var infowindow = new google.maps.InfoWindow({
});
google.maps.event.addListener(marker,'click',function()
{
//console.log(infowindow.getContent());
infowindow.setContent(contentArray[i]);
infowindow.open(map,this);
});
markers.push(marker);
}
else
{
console.log(latlngArray[i]);
var marker = new google.maps.Marker({
position:latlngArray[i],
animation:google.maps.Animation.DROP,
icon:'./images/restaurant.png',
title:titleArray[i],
map:map
});
var infowindow = new google.maps.InfoWindow({});
//console.log(infowindow.getContent());
google.maps.event.addListener(marker,'click',function()
{
infowindow.setContent(contentArray[i]);
console.log(infowindow.getContent());
infowindow.open(map,this);
});
markers.push(marker);
}
//console.log(i);
//console.log(contentArray[i]);
}
}
The problem is that when the loop ends, i is 10.
Every infowindow displays:
infowindow.setContent(contentArray[i]);
There are two ways to solve the problem:
function closure. Use a createMarker function to associate the infowindow content with the marker. Explained in Mike Williams' v2 tutorial, one of his examples using function closure, translated to v3.
marker member variable containing the content, access it in the click listener by referencing "this". The answer to this similar question may help with this. Here is an example of using a member variable of the marker
This Code is also for all those who want to put multiple Markers on Map retrieved from DB
i am going to paste a Code of live project means working. you can get some help from this.
function latLongCallback(latitutde,longitutde){
var latlng = new google.maps.LatLng(latitutde, longitutde);
var options = {zoom: 4, center: latlng, mapTypeId: google.maps.MapTypeId.ROADMAP};
var map = new google.maps.Map(document.getElementById('map'), options);
$.ajax({type: "GET",
dataType: 'json',
url: 'https://www.xyz.com/yourrfolder/markers.php',
success: function(response){
var total=response.length;
var data_array,name,type,address,lat,lon,arrival,departure,notes;
var infowindow = new google.maps.InfoWindow();
for(var i=0; i < total; i++){
data_array=response[i];
name=data_array['name'];
id = data_array['id'];
address=data_array['address'];
arrival=data_array['arrival'];
departure=data_array['departure'];
notes=data_array['notes'];
lat=data_array['lat'];
lon=data_array['lon'];
icon=data_array['icon'];
sc_id=data_array['sc_id'];
var propPos = new google.maps.LatLng(lat,lon);
propMarker = new google.maps.Marker({
position: propPos,
map: map,
icon: icon,
zIndex: 3
});
var contentString = "<div style='font-size:9px;overflow:hidden'>"+name+"<br/><label class='label'>Location :</label> "+address+"<br/><label class='label'>Arrival :</label> "+arrival+"<br/><label class='label'>Departure :</label> "+departure+"<br/><label class='label'>Notes :</label> "+notes + "</div><div style='font-size:9px;overflow:hidden'><a href='#2' onclick="+xx+" class='popup-txt' style='font-size:11px; margin-top:3px;'>Message him</a><a href='#1' onclick="+invite+" class='popup-txt' style='font-size:11px; margin-top:3px; float:right;'>Invite Friend</a></div>";
function bindInfoWindow(marker, map, infowindow, html) {
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(html);
infowindow.open(map, marker);
});
bindInfoWindow(propMarker, map, infowindow, contentString);
}
}
});
return;
}
and here is the marker.php mentioned in above js
<?php
$data=array();
$retrive_marker_query = "your query";
$result = db_execute($retrive_marker_query);
$cnt=0;
while ($row = mysql_fetch_assoc($result)){
$name = $row['name'];
$id = $row['fb_id'];
$sc_id = $row['id'];
$address = $row['location'];
$lat = $row['lat'];
$lon = $row['lon'];
$data[$cnt]['name'] = $name;
$data[$cnt]['id'] = $id;
$data[$cnt]['sc_id'] = $sc_id;
$data[$cnt]['address'] = $address;
$data[$cnt]['lat'] = $lat;
$data[$cnt]['lon'] = $lon;
$cnt++;
}
$data=json_encode($data);
echo($data);
<?

How do I update the locations of multiple markers in google maps

I am using google maps api to place markers on a map. The gps coordinates of the markers are stored in a mySQL database. I have been able to create the markers however the locations will constantly changing so I was wondering how I would go about updating the markers' locations so that the markers will move across the map. Here is my code so far:
<!DOCTYPE html>
<html>
<head><title>Map</title>
<style type="text/css">
html { height: 100% }
body { height: 100%; margin: 0px; padding: 0px }
#map_canvas { height: 100% }
</style>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false">
</script>
<script type="text/javascript">
function initialize() {
var latlng = new google.maps.LatLng(36.9947935, -122.0622702);
var myOptions = {
zoom: 15,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
var image = new google.maps.MarkerImage('busIcon.png',
// The image size
new google.maps.Size(44, 46),
// The origin
new google.maps.Point(0,0),
// The anchor
new google.maps.Point(22, 23));
var shadow = new google.maps.MarkerImage('busIcon_shadow.png',
new google.maps.Size(58, 46),
new google.maps.Point(0,0),
new google.maps.Point(22, 23)
);
var shape = {
coord: [1, 1, 1, 45, 43, 45, 43 , 1],
type: 'poly'
};
var markers = [
<?php
// Make a MySQL Connection
mysql_connect("**********", "**********", "**********") or die(mysql_error());
mysql_select_db("matsallen") or die(mysql_error());
//Get number of rows
$result = mysql_query
(
'SELECT COUNT(*) FROM busTrack AS count'
)
or die(mysql_error());
$row = mysql_fetch_array( $result );
$length = $row["COUNT(*)"];
for ($count = 1; $count <= $length; $count++) {
//Get data from MySQL database
$result = mysql_query
(
'SELECT * FROM busTrack WHERE busID = '.$count
)
or die(mysql_error());
// store the record of the "busTrack" table into $row
$row = mysql_fetch_array( $result );
// Echo the data into the array 'markers'
$output = '{id: "Bus '.$row[busID].'", lat: '.$row[lat].', lng: '.$row[lon].'}';
if ($count != $length) {
$output = $output.',';
};
echo $output;
};
?>
];
for (index in markers) addMarker(map, markers[index]);
function addMarker(map, data) {
//create the markers
var marker = new google.maps.Marker({
position: new google.maps.LatLng(data.lat, data.lng),
map: map,
title: data.id,
icon: image,
shape: shape,
shadow: shadow
});
//create the info windows
var content = document.createElement("DIV");
var title = document.createElement("DIV");
title.innerHTML = data.id;
content.appendChild(title);
var infowindow = new google.maps.InfoWindow({
content: content
});
// Open the infowindow on marker click
google.maps.event.addListener(marker, "click", function() {
infowindow.open(map, marker);
});
}
}
</script>
</head>
<body onload="initialize()">
<div id="map_canvas" style="width:75%; height:75%; margin:10%; allign:center;"></div>
</body>
</html>
To change the location of a marker, call setPosition() on that marker:
marker.setPosition(new google.maps.LatLng(newLat,newLng);
To do this, you will need to save all your markers in an array. Perhaps something like this would work:
var myMarkers = new Array();
for (index in markers) {
myMarker[ markers[index]['id'] ] = addMarker(map, markers[index]);
}
function addMarker(map, data) {
//create the markers
var marker = new google.maps.Marker({
position: new google.maps.LatLng(data.lat, data.lng),
map: map,
title: data.id,
icon: image,
shape: shape,
shadow: shadow
});
//create the info windows
var content = document.createElement("DIV");
var title = document.createElement("DIV");
title.innerHTML = data.id;
content.appendChild(title);
var infowindow = new google.maps.InfoWindow({
content: content
});
// Open the infowindow on marker click
google.maps.event.addListener(marker, "click", function() {
infowindow.open(map, marker);
});
return marker;
}
Then, when the position of a marker needs to change, hopefully you know the bus ID and can just call:
myMarkers['Bus 47'].setPosition(new google.maps.LatLng(newLat,newLng);
I didn't test the above code so there may be small syntax errors or something, but it should give you the idea.

Categories

Resources