I am working on amcharts map
in angular
. I am binding locations using on lat
and long
, I don't have countries id
rather have location name. I want to zoom specific location using zoomToMapObject
.
I have dropdown, when I select one country from it, I want to zoom that.
This is my object
of locationData
{
"dId": 28046,
"lat": 33.8627681,
"long": -117.8870831,
"loc": "Fullerton CA"
"color":"red"
},
{
"dId": 25535,
"lat": 31.5649775,
"long": -110.2591639,
"loc": "Sierra Vista AZ"
"color":"yellow"
},
"dId": 31222,
"lat": 33.4308114,
"long": -70.7828727,
"loc": "Pudahuel Metropolitana"
"color":"green"
},
{
"dId": 23280,
"lat": 36.1676717,
"long": -94.1298177,
"loc": "Springdale AR"
"color":"red"
},
When I select dropdown, In SelectCountry
how can I get my code work. These all variables are returning undefined
, hence zoomToMapObject
not working
public mapCountry_selected(country)
{
setTimeout( () => {
this.mapChart.zoomToGeoPoint({latitude:country.lat,longitude:country.long},17,true)
}, 100);
}
This is my code
public locationMap()
{
this.mapChart = am4core.create("productsMap", am4maps.MapChart);
this.mapChart .geodata = am4geodata_worldLow;
this.mapChart .projection = new am4maps.projections.Miller();
this.polygonSeries = this.mapChart .series.push(new am4maps.MapPolygonSeries());
this.polygonSeries.exclude = ["AQ"];
this.polygonSeries.useGeodata = true;
this.polygonSeries.calculateVisualCenter = true;
let imageSeries = this.mapChart .series.push(new am4maps.MapImageSeries());
var place = imageSeries.mapImages.template;
place.nonScaling = true;
place.propertyFields.latitude = "lat";
place.propertyFields.longitude = "long";
imageSeries.data=this.locationData;
var circle = place.createChild(am4core.Circle);
circle.propertyFields.fill = "color";
imageSeries.heatRules.push({
"target": circle,
"property": "radius",
"min": 3,
"max": 10,
"dataField": "value",
})
}
Edit1:
I am showing circle for every location("target": circle
) with three colors green, yellow, red. Now after adding true
point 2nd and 3rd solved(from my comment). I need highlight/popover location after selecting one from dropdown so user can directly will come know this location I have selected as there are many locations around each other.
Edit2:
Map after selecting location from dropdown
Based on what information you have available it may be easier to consider using the zoomToGeoPoint method available on the MapChart object eg
//assuming the selected country is as follows
var country = {
"dId": 23280,
"lat": 36.1676717,
"long": -94.1298177,
"loc": "Springdale AR",
"color":"red",
};
this.mapChart.zoomToGeoPoint({latitude:country.lat,longitude:country.long},17)
where 17
is your desired zoom level. zoomToMapObject
is especially useful when handling chart events.
However, if you would like to use the zoomToMapObject method, Based on your example the following approach could be considered.
For each data point in your dataset, and using the initial data stored in am4geodata_worldLow
which has the "id" (eg. extracting the id for the first am4geodata_worldLow.features[0].id
) for each polygon, you could determine for each country/place using the lat/lng coordinates which is closest to the center of each polygon.
In the MapChart
object you have stored in this.mapChart
there are multiple series (vaguely considered as layers) which you would have to check for polygon or MapObjects. These can be extracted using the following:
var mapPolygons = this.mapChart.series.values.flatMap(function(seriesItem){return seriesItem.mapPolygons.values;});
You could then build a data structure mapping each of your custom country/places to the ids available in the map (for eg an object {"your-country-id":mapPolygonObject}
)
For each mapPolygon
you can extract coordinate points using
var coordinates = mapPolygon.points[0][0]; //returns an array of x,y coordinates
//eg
coordinates =
[{"x":691.2713756097037,"y":137.68935508639663},{"x":691.2544487420046,"y":137.69150482342081},{"x":691.2544487420046,"y":137.67239542245633},{"x":691.2695833531237,"y":137.6702455263596}];
You could then find the center/centroid of this polygon using these points as bounds and then for each of your country points you would find the euclidean distance between the polygon center point and your country. The shortest distance could assume that this country is associated with this point. Considerations may be made for distance (NB. Since you are working with coordinates, be sure to consider to 5 decimal points at least) as 50,000 miles (please observe your coordinate system for ways to convert lat/lng distances to metres/miles) may mean these are different places.
You could then extract the ids and build your map object for use later in your script. This may be time consuming and compute intensive and ideally would be better if you did this before had and stored the necessary references to be used later.
Moreover, if you had the country ids, then this would be simpler search as denoted below:
//assuming the selected country is as follows
var country = {
"dId": 23280,
"lat": 36.1676717,
"long": -94.1298177,
"loc": "Springdale AR",
"color":"red",
};
//for this example I am extracting the country id from the object,it would be ideal to have it otherwise
//NB. country ids are also available in the `am4geodata_worldLow` used to initialize the map
var countryId = country.loc.split(" ")[1]; //this would return the country code - 'AR'
//we will now search the created chart for mapObjects with this id
//this will give us an array of map objects/polygons with the id if found
var possibleMapObjects = this.mapChart.series.values.map(function(seriesItem){
return seriesItem.getPolygonById(countryId)
}).flat();
if(possibleMapObjects.length > 0){
// we have found a map object
var countryMapObject = possibleMapObjects[0]; //taking the first item of array
//now we can zoom to map object
//the 3rd parameter true implies centering the map
this.mapChart.zoomToMapObject(countryMapObject,17,true)
}
Based on the updated code, I noticed a few issues, I was unable to see markers on your map so i made a few modifications. Also, the modifications now allow you to retrieve marker/map objects by id as I have augmented the data with the id you have used please see updated code below.
I have created a dropdown/select node to simulate the onchange event, in the callback/event handler, onCountryChange
you will see how we can retrieve a marker by it's new pseudo-id that we specified and call actions (I will open a popup) according to the Documented MapImage API
locationData = [
{
"dId": 28046,
"lat": 33.8627681,
"long": -117.8870831,
"loc": "Fullerton CA",
"color": "red"
},
{
"dId": 25535,
"lat": 31.5649775,
"long": -110.2591639,
"loc": "Sierra Vista AZ",
"color": "yellow"
},
{
"dId": 31222,
"lat": 33.4308114,
"long": -70.7828727,
"loc": "Pudahuel Metropolitana",
"color": "green"
},
{
"dId": 23280,
"lat": 36.1676717,
"long": -94.1298177,
"loc": "Springdale AR",
"color": "red"
},
].map(function(place) {
place.id = place.dId;
return place;
});
this.mapChart = am4core.create("productsMap", am4maps.MapChart);
this.mapChart.geodata = am4geodata_worldLow;
this.mapChart.projection = new am4maps.projections.Miller();
this.polygonSeries = this.mapChart.series.push(new am4maps.MapPolygonSeries());
this.polygonSeries.exclude = ["AQ"];
this.polygonSeries.useGeodata = true;
this.polygonSeries.calculateVisualCenter = true;
let imageSeries = this.mapChart.series.push(new am4maps.MapImageSeries());
var place = imageSeries.mapImages.template;
place.nonScaling = true;
place.propertyFields.latitude = "lat";
place.propertyFields.longitude = "long";
imageSeries.data = locationData;
var circle = place.createChild(am4core.Circle);
circle.propertyFields.fill = "color";
/// code below made circles visible and used value in {loc} / location name as tooltiptext
circle.width = 20;
circle.height = 20;
circle.nonScaling = false;
circle.tooltipText = "{loc}";
circle.horizontalCenter = "middle";
circle.verticalCenter = "bottom";
var circle2 = imageSeries.mapImages.template.createChild(am4core.Circle);
circle2.radius = 3;
circle2.propertyFields.fill = "color";
circle2.events.on("inited", function(event){
animateBullet(event.target);
})
function animateBullet(circle) {
var animation = circle.animate([{ property: "scale", from: 1, to: 5 },
{property: "opacity", from: 1, to: 0 }], 1000, am4core.ease.circleOut);
animation.events.on("animationended", function(event){
animateBullet(event.target.object);
})
}
imageSeries.heatRules.push({
"target": circle,
"property": "radius",
"min": 3,
"max": 10,
"dataField": "value",
});
//just initializing the ui (you have already done this in angular)
var ddlPlaceChooser = document.getElementById("ddlPlaceChooser");
locationData.map(function(location) {
var option = document.createElement("option");
option.value = location.id;
option.innerText = location.loc;
return option;
})
.forEach(function(option) {
ddlPlaceChooser.appendChild(option)
})
function onCountryChange(event) {
//however you retrieve the selected id
var placeId = ddlPlaceChooser.selectedOptions[0].value;
//retrieve map object
var mapObject = imageSeries.getImageById(placeId);
//zoom to map object
mapChart.zoomToMapObject(mapObject, 17, true);
//optionally open popup with message of choice
mapObject.openModal("You choose me!")
//mapObject.openPopup("You choose me!")
}
ddlPlaceChooser.addEventListener('change', onCountryChange)
<script src="https://www.amcharts.com/lib/4/core.js"></script>
<script src="https://www.amcharts.com/lib/4/maps.js"></script>
<script src="https://www.amcharts.com/lib/4/geodata/worldLow.js"></script>
<script src="https://cdn.amcharts.com/lib/4/themes/animated.js"></script>
<div>
<label for="placeChooser">Choose Place</label>
<select id="ddlPlaceChooser" name="placeChooser"></select>
</div>
<hr />
<div id="productsMap"></div>