Adding geometries to data layer - how to set feature styles during add? - google-maps

I'm building a school layer. For each school, I will assign a different icon depending on its properties.
This is my current solution. I first insert all the schools into a data layer, and then run a forEach function to change each point's icon. This is not optimal, because I am adding the schools, then immediately editing the schools.
// Current solution. It is WORKING but it is not optimal
schools = (a list of google.maps.Data.Point objects)
for (school in schools) {
schoolLayer.add({
geometry: school,
});
}
schoolLayer.forEach(function(feature) {
schoolLayer.overrideStyle(feature, {
if (some condition) {
icon: ...
} else {
icon: ...
}
}
}
The optimal solution would be to add styles to the school while it is added, so that no editing is required afterwards. Something like this:
// The solution I am trying to achieve. The `StyleOptions` property is made up to represent what I am trying to achieve. I want to add styles to the school as it is being inserted into the data layer
for (school in schools) {
schoolLayer.add({
geometry: school,
StyleOptions: {
if (some condition) {
icon: ...
} else {
icon: ...
}
}
});
}
The above code does not work. Is there something I'm missing from the documentation that allows me to achieve that?

I would suggest creating a list of google.maps.Data.Feature objects instead of the google.maps.Data.Point objects. Data.Feature can include geometry that is an instance of google.maps.Data.Point in your case, id that can be string or number and properties where you can put name-value pairs.
https://developers.google.com/maps/documentation/javascript/reference/3/data#Data.Feature
The presence of properties in a feature makes a trick. You can apply a styling function for your data layer that reads property of the feature (icon in your case) and returns corresponding style. The styling function will be applied when you add a feature to the data layer.
Have a look at the following code sample, the schoolLayer.setStyle() is the most relevant part
var map;
function initMap() {
var schools = [
new google.maps.Data.Feature({
geometry: new google.maps.Data.Point({lat: 41.384301, lng: 2.173792}),
id: 1,
properties: {
"icon": "http://maps.google.com/mapfiles/kml/paddle/blu-blank.png"
}
}),
new google.maps.Data.Feature({
geometry: new google.maps.Data.Point({lat: 41.384897, lng: 2.176656}),
id: 2,
properties: {
"icon": "http://maps.google.com/mapfiles/kml/paddle/pink-blank.png"
}
}),
new google.maps.Data.Feature({
geometry: new google.maps.Data.Point({lat: 41.386756, lng: 2.175268}),
id: 3,
properties: {
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/geocode-71.png"
}
})
];
map = new google.maps.Map(document.getElementById('map'), {
zoom: 17,
center: {lat: 41.385064, lng: 2.173403}
});
var schoolLayer = map.data;
schoolLayer.setStyle(function(feature){
return {
icon: feature.getProperty("icon"),
title: "" + feature.getId()
};
});
for (school of schools) {
schoolLayer.add(school);
}
}
#map {
height: 100%;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
}
<div id="map"></div>
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDztlrk_3CnzGHo7CFvLFqE_2bUKEq1JEU&callback=initMap">
You can also find this example on jsfiddle: https://jsfiddle.net/xomena/tLsjkowp/
I hope this helps!

#xonema - Great answer.
Just adding that I set the properties locally in this manner:
new google.maps.Data.Feature({
geometry: new google.maps.Data.Point(gaugeLatLng),
properties: {
title: gaugeDetail.gaugeId,
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: 6,
},
}
})

Related

How to add map markers stored in a JSON feed to an existing React Google Map?

I have a react-google-maps project which creates a map:
const MarkerComponent = ({text}) => <div>{text}</div>;
export default class NavMap extends Component {
constructor(props) {
super(props);
this.state = {
markers: []
}
}
static defaultProps = {
center: {lat: -41.25,lng: 173.2},
zoom: 11
}
render() {
return (<div id="nav-map" className='google-map'>
<GoogleMapReact
name={map}
apiKey={'MYAPIKEY'}
defaultCenter={this.props.center}
defaultZoom={this.props.zoom}>
<MarkerComponent lat={-41.25} lng={173.2} text={'Centre'}/>
</GoogleMapReact>
</div>)
}
}
This will add a text marker to the centre of the map.
However I can't work how to add the markers from a dynamic JSON feed that is loaded after the map is created/ loaded in React. Please note the JSON feed could be updated - at which point the markers would be refreshed.
In React I normally call a JSON feed like this:
componentDidMount() {
fetch('/myJSONfeed').then(response => response.json()).then(data => {
this.setState({data});
});
}
I've had a good look around online for a solution but have not being able to work out how to add dynamic markers after the map has been created/ loaded.
Any ideas or example code would be appreciated.
I have encountered the same issue recently. Hope this explanation will help you to figure out what went wrong.
Overview
When we are working with external sources. It's important to note that nothing guarantees you the order of tasks to be executed. As in your case, fetching a JSON feed is fetched asynchronously.
The problem
Fetching the feed inside componentDidMount() should be fine. However, you still need to wait for the data to available. Hence, you should tell others components to listen for that event, and update their properties thereafter.
The Solution
By using the componentDidMount() we can wait for the map to load, and properties to propagate into the components. Then, with componentDidUpdate() we can operate on the DOM.
It goes like this:
In App.js:
componentDidMount(){
fetch(THE_SOURCE_TO_BE_FETCHED)
.then(function(response) {
return response.json();
}).then(data => {
this.setState({markers: data});
});
}
In the Map component
componentDidUpdate(){
const google = window.google;
this.map = new google.maps.Map(this.refs.map, {
center: this.props.center,
zoom: 4
});
this.createMarkers(this.props.markers)
}
createMarkers(users){
const google = window.google;
users.map(user => {
this.marker = new google.maps.Marker({
position: {
lat: user.location.latitude,
lng: user.location.longitude
},
map: this.map,
});
this.state.markers.push(this.marker);
})
}
Please note: you should carefully examine your JSON and check that it is valid, as well as if it subject to be parsed from string etc.
Take a tour at React's docs of React's component lifecycle if you need more details.
GL & HF

How do I import a csv into chart.js?

I have been looking for this solution but can't seem to find it . Does chart.js support this ?
I have attempted to parse in the data with papaparse, but due to my limited knowledge I can't find a solution.
There is a Chart.js plugin chartjs-plugin-datasource that makes it easy to load data from CSV files.
Let's suppose you have a CSV file as shown below, and it is saved as sample-dataset.csv in the same directory as your HTML file:
,January,February,March,April,May,June,July
Temperature,7,7,10,15,20,23,26
Precipitation,8.1,14.9,41.0,31.4,42.6,57.5,36.0
Include Chart.js and chartjs-plugin-datasource in your page:
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datasource#0.1.0"></script>
<canvas id="myChart"></canvas>
Then, specify sample-dataset.csv in your script. By default, each row in a CSV file will be mapped to one dataset, and the first column and the first row will be treated as dataset labels and index labels respectively. You can also customize how to parse CSV data using options.
var ctx = document.getElementsById("myChart");
var chart = new Chart(ctx, {
type: 'bar',
data: {
datasets: [{
type: 'line',
yAxisID: 'temperature',
backgroundColor: 'transparent',
borderColor: 'rgb(255, 99, 132)',
pointBackgroundColor: 'rgb(255, 99, 132)',
tension: 0,
fill: false
}, {
yAxisID: 'precipitation',
backgroundColor: 'rgba(54, 162, 235, 0.5)',
borderColor: 'transparent'
}]
},
plugins: [ChartDataSource],
options: {
scales: {
yAxes: [{
id: 'temperature',
gridLines: {
drawOnChartArea: false
}
}, {
id: 'precipitation',
position: 'right',
gridLines: {
drawOnChartArea: false
}
}]
},
plugins: {
datasource: {
url: 'sample-dataset.csv'
}
}
}
});
Here is my solution that works fine for me. I have a CSV file like this:
country,population
China,1415046
India,1354052
United States,326767
Indonesia,266795
Brazil,210868
...
I want to plot a bar chart with my dataset, the y-axis is population and the x-axis is country.
This is the body of my HTML file.
<body>
<canvas id="myChart" width="100" height="100"></canvas>
<script>
// Load the dataset
d3.csv("data.csv").then(makeChart);
// Plot the data with Chart.js
function makeChart(countries) {
var countryLabels = countries.map(function (d) {
return d.country;
});
var populationData = countries.map(function (d) {
return d.population;
});
var chart = new Chart("myChart", {
type: "bar",
data: {
labels: countryLabels,
datasets: [
{
data: populationData
}
]
}
});
}
</script>
</body>
Result:
You can try it with Codesandbox.
I've had need to do something like this from time to time as well. Here's a link on how to create a chart with Chart.js from a CSV file that explains exactly how to create a chart with Chart.js directly from a CSV file.
The use case explains how to convert a CSV file to JSON using the Flex.io web service (Full disclosure: I'm the senior front end developer at Flex.io).
Here's a JsFiddle if you'd like to see the use case in action:
function getRandomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
$.ajax({
type: 'post',
url: 'https://www.flex.io/api/v1/pipes/flexio-chart-js-csv-to-json/run?stream=0',
beforeSend: function(xhr) {
xhr.setRequestHeader('Authorization', 'Bearer nmgzsqppgwqbvkfhjdjd');
},
data: $('form').serialize(),
dataType: "json",
success: function(content) {
// render the JSON result from from the Flex.io pipe
$("#flexio-result-data").text(JSON.stringify(content, null, 2))
var first_item = _.get(content, '[0]', {})
var column_labels = _.map(_.omit(first_item, ['os']), function(val, key) {
if (key != 'os')
return key
})
// use Lodash to reformat the JSON for use with Chart.js
var datasets = _.map(content, function(item) {
// use the 'os' column as our label
var item_label = _.get(item, 'os', 'Not Found')
// create an array of number values from each item's JSON object
var item_values = _.map(_.omit(item, ['os']), function(val) {
return parseFloat(val)
})
return {
label: item_label,
data: item_values,
backgroundColor: getRandomColor()
}
})
var chart_data = {
labels: column_labels,
datasets: datasets
}
// render the JSON result after mapping the data with Lodash
$("#chart-data").text(JSON.stringify(chart_data, null, 2))
// render the chart using Chart.js
var ctx = document.getElementById("canvas").getContext("2d");
window.my_chart = new Chart(ctx, {
type: 'bar',
data: chart_data,
options: {
responsive: true,
legend: {
position: 'top'
},
title: {
display: true,
text: 'Use Flex.io to Create a Chart With Chart.js Directly From a CSV File'
}
}
});
}
});
Feel free to step through the use case and let me know if you have any issues.
The simple example of importing CSV data into ChartJS
index.html:
<!-- ChartJS plugin datasrouce example
chartjs-plugin-datasource: https://nagix.github.io/chartjs-plugin-datasource/
Samples: https://nagix.github.io/chartjs-plugin-datasource/samples/
Specific example: https://nagix.github.io/chartjs-plugin-datasource/samples/csv-index.html
Data source: https://gist.githubusercontent.com/mikbuch/32862308f4f5cac8141ad3ae49e0920c/raw/b2b256d69a52dd202668fe0343ded98371a35b15/sample-index.csv -->
<head>
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datasource#0.1.0"></script>
</head>
<body>
<div>
<canvas id="myChart"></canvas>
</div>
<script src="script.js"></script>
</body>
You can also download this index.html file as a gist.
script.js
// ChartJS plugin datasrouce example
// chartjs-plugin-datasource: https://nagix.github.io/chartjs-plugin-datasource/
// Samples: https://nagix.github.io/chartjs-plugin-datasource/samples/
// Specific example: https://nagix.github.io/chartjs-plugin-datasource/samples/csv-index.html
// Data source: https://gist.githubusercontent.com/mikbuch/32862308f4f5cac8141ad3ae49e0920c/raw/b2b256d69a52dd202668fe0343ded98371a35b15/sample-index.csv
var chartColors = {
red: 'rgb(255, 99, 132)',
blue: 'rgb(54, 162, 235)'
};
var color = Chart.helpers.color;
var config = {
type: 'bar',
data: {
datasets: [{
type: 'line',
yAxisID: 'temperature',
backgroundColor: 'transparent',
borderColor: chartColors.red,
pointBackgroundColor: chartColors.red,
tension: 0,
fill: false
}, {
yAxisID: 'precipitation',
backgroundColor: color(chartColors.blue).alpha(0.5).rgbString(),
borderColor: 'transparent'
}]
},
plugins: [ChartDataSource],
options: {
title: {
display: true,
text: 'CSV data source (index) sample'
},
scales: {
xAxes: [{
scaleLabel: {
display: true,
labelString: 'Month'
}
}],
yAxes: [{
id: 'temperature',
gridLines: {
drawOnChartArea: false
},
scaleLabel: {
display: true,
labelString: 'Temperature (°C)'
}
}, {
id: 'precipitation',
position: 'right',
gridLines: {
drawOnChartArea: false
},
scaleLabel: {
display: true,
labelString: 'Precipitation (mm)'
}
}]
},
plugins: {
datasource: {
type: 'csv',
url: 'https://gist.githubusercontent.com/mikbuch/32862308f4f5cac8141ad3ae49e0920c/raw/b2b256d69a52dd202668fe0343ded98371a35b15/sample-index.csv',
delimiter: ',',
rowMapping: 'index',
datasetLabels: true,
indexLabels: true
}
}
}
};
window.onload = function() {
var ctx = document.getElementById('myChart').getContext('2d');
window.myChart = new Chart(ctx, config);
};
Here is a a gist with this script.js file.
Make sure that both files are in the same directory.
Open index.html with your browser.
Additional materials
CodeSandbox example to preview the example online.
Reason for posting this answer:
I posted this because people are having problems with reading CSV files from the filesystem (directly from the computer) with JavaScript. The examples in chartjs-plugin-datasource documentation don't explain this, and it is assumed that the user has some basic knowledge on the differences in handling URLs from the web, and files from the file system.
My example just shows the basic functionality of the ChartJS datasource plugin, no third-party modules for reading the CSV file are required.
Edit:
According to ggorlen's suggestion from the comment, I also included the code snippets in the answer itself.
Can't post comments on this elitist site, because four years hasn't gotten me enough "points." ....
#huy - interesting how your Codesandbox has completely different code than what you've posted which I found was directly ripped off from another site... it doesn't even relate to the chart you were talking about building. Within Codesandbox, all I see is an image file of the chart, nothing is actually working (and how could it, when the code isn't even related to your dataset!?).

JSON in Jvector Map How to resolve error, TypeError: jvm.WorldMap is not a constructor

In the JVectorMap I want to add the Markers from JSON. I found an idea here on Github.
This is the code I am using:
$.getJSON('/Ajax/Dealers.json', function (data) {
var map = new jvm.WorldMap({
map: 'world_mill_en',
container: $('#world-map'),
markerStyle: {
initial: {
fill: '#4087bd',
stroke: '#28456f'
}
},
series: {
regions: [{
attribute: 'fill'
}]
},
markers: data
});
map.series.regions[0].setValues(customColors);
});
But the problem is I am getting error.
TypeError: jvm.WorldMap is not a constructor
On this line:
var map = new jvm.WorldMap({
I think you forgot to include library file or its not in correct location. Include this library file and then try
http://jvectormap.com/js/jquery-jvectormap-1.2.2.min.js

Current location in sencha touch

I am using the following code to display my current location on Sencha Touch 2. Its showing the correct latitude in console.log() but not showing the map. Please help.
Ext.define('restApp.view.GreetingView', {
extend: 'Ext.Map',
alias: 'widget.mymap',
config: {
fullscreen: true,
//tpl: '<p>The ID is {uuid}</p><p>The content is {display}</p>',
layout: {
type: 'fit'
}
},
initialize:function(){
Ext.Viewport.add({
xtype: 'map',
id:'geomap',
itemId:'ma'
});
var geo = Ext.create('Ext.util.Geolocation', {
autoUpdate: true,
frequency: '10000',
listeners: {
locationupdate: function (geo) {
var center = new google.maps.LatLng(geo.getLatitude(), geo.getLongitude());
Ext.getCmp('geomap').setData(center);
//restApp.view.GreetingView.getComponent('ma').update(center);
var marker = new google.maps.Marker({
position: center,
map: Ext.getCmp('geomap').map
});
console.log('New latitude: '+ geo.getLatitude());
},
locationerror: function (geo, bTimeout, bPermissionDenied, bLocationUnavailable, message) {
if (bTimeout) {
alert('Timeout occurred.');
}
else {
alert('Error occurred.');
}
}
}
});
}
});
OLD ANSWER
Comparing it to a snippet I use for the same purpose (shown below), I realised the issue is a simple one. "center" is a reserved word. Try using a different variable name.
PART OF EDIT: removal of code snippets.
NEW ANSWER
I looked around and noticed your "project" is but a piecemeal collection of demo code.
Here's a complete code solution, with all excess pieces removed for simplicity, as well as over use of variables, also expanded to a longwinded format to be obvious.
/*
The application, including a simple launcher.
*/
Ext.application({
requires : ['Ext.device.Geolocation'],
views : ['MainView'],
controllers : ['Maps'],
name : 'Geo',
launch: function() {
Ext.create('Geo.view.MainView', {fullscreen: true});
}
});
/*
The View to display the map, as well as how to include the navigation bar
and include a "you are here" button.
*/
Ext.define('Geo.view.MainView', {
extend: 'Ext.navigation.View',
alias: 'widget.mainview',
requires: [
'Ext.Panel',
'Ext.Map',
'Ext.navigation.Bar',
'Ext.Button'
],
config: {
items: [
{
xtype : 'panel',
title : 'Map',
itemId : 'mapPanel',
items : [
{
xtype: 'map',
height: '100%',
itemId: 'map'
}
]
}
],
navigationBar: {
docked: 'top',
items: [
{
xtype: 'button',
itemId: 'youAreHereButton',
text: 'You Are Here'
}
]
}
}
});
/*
The Controller with functionality for the "you are here" button tap
*/
Ext.define('Geo.controller.Maps', {
extend: 'Ext.app.Controller',
config: {
refs: {
mapView: {
selector: 'mainview #map',
xtype: 'Ext.Map'
},
mainView: {
selector: 'mainview',
xtype: 'Ext.navigation.View'
}
},
control: {
"mainview #youAreHereButton": {
tap: 'onYouAreHereTap'
}
}
},
onYouAreHereTap: function(button, e, eOpts) {
// set 'mapView' as the parent view displaying the map
var mapView = this.getMapView();
// control measure for old browsers or denied permission for location detection.
if (Ext.feature.has.Geolocation) {
/*
Ext.device.Geolocation uses native (phone) Geolocation capabilities if available,
and falls back to Ext.util.Geolocation if only browser support detected.
*/
Ext.device.Geolocation.getCurrentPosition({
allowHighAccuracy : true,
maximumAge : 0,
timeout : 20000,
success : function(position) {
var latitude = position.coords.latitude,
longitude = position.coords.longitude,
location = new google.maps.LatLng(latitude, longitude),
marker = new google.maps.Marker({
position : location,
map : mapView.getMap(),
animation : google.maps.Animation.DROP
});
mapView.setMapOptions({ // Move to the center
center: location
});
},
failure: function() {
console.log('something went wrong!');
}
});
}
}
});
Yes, I could have simplified it further down to a single view, containing also the controller's handler for the "you are here" tap. I have chosen to present it this way to assist you with understanding the MVC pattern and how it applies in Sencha Touch.
For this to work, it'll require the Sencha Touch library, as well as this following line:
<script src="http://maps.google.com/maps/api/js?sensor=true"></script>
This is the script which includes Google Maps in your page and is essential for displaying.
Learn more:
https://www.sencha.com/learn/hello-world/ - getting started
http://docs.sencha.com/touch/2.3.1/#!/guide - complete documentation for how to do anything in Sencha Touch, starting with the Guides page.

Sencha Touch - Add Markers to Map after Maprender

I want to load markers to my map, in sencha touch, after I load some json data from ajax request. The thing is I don't know how to achieve this after the maprender event is fired.
I've got this:
my map view:
app.views.MapTab = Ext.extend(Ext.Panel, {
iconCls: 'map',
id: 'map',
items: [{
xtype: 'map',
id: 'mapa',
mapOptions : {
center : new google.maps.LatLng(50.077721, 14.448585),
zoom : 12,
mapTypeId : google.maps.MapTypeId.ROADMAP,
navigationControl: true,
navigationControlOptions: {
style: google.maps.NavigationControlStyle.DEFAULT
}
},
listeners : {
'maprender' : function(comp, map){
Ext.dispatch({
controller: app.controllers.map,
action: 'map_rendered',
map: map
});
}
}
}],
initComponent: function() {
app.views.MapTab.superclass.initComponent.apply(this, arguments);
}
});
store to load json:
app.stores.results = new Ext.data.Store({
model: "app.models.Results",
proxy: {
type: 'ajax',
url: 'http://app.cz/json_list2.php?a=l&zp=1&u=vinohrady-praha&c0=500000&c1=6000000&p0=10&p1=120&cm20=1000&cm21=120000&pg=0&s=Datumu&t=byt&age=30&pod=0&lat=50.075401&lng=14.458344&pp=prodej&tp0=0&tp1=12',
reader: {
type: 'json',
root: 'markers'
}
},
listeners: {
'load': function (t, r, s) {
Ext.dispatch({
controller: app.controllers.map,
action: 'loaded',
records: r
});
}
}
});
controller
app.controllers.map = new Ext.Controller({
loaded: function(options) {
for(var i = 0; i < options.records.length; i++){
var marker = new google.maps.Marker({
position: new google.maps.LatLng(options.records[i].get('lat'), options.records[i].get('lng')),
map: app.views.mapTab.getComponent('mapa')
});
}
// need to rerender/refresh the map here
},
map_rendered: function(options) {
console.log("map rendered");
}
});
Your code should work but you are attaching the markers to the Sencha Touch Map component rather than the underlying Google Maps map, which can be accessed with the 'map' property.
...
var marker = new google.maps.Marker({
position: new google.maps.LatLng(options.records[i].get('lat'), options.records[i].get('lng')),
**map: app.views.mapTab.getComponent('mapa').map**
});
...
The map shouldn't need manually re-rendered and should just add them in when you bind them with the map config.