I have a mapbox map in my Ruby on Rails app, and I'd like to populate a feature layer with point coordinates from my database. I can't seem to find meaningful help on this on the Mapbox website. I found a website that offered a guide, but it skips steps that must have been obvious to the author, but aren't to me. Can anyone help me do this?
Here's what I have. In the Index method of my Productions controller, I've created a geojson array (the indentation here is screwed up, but it's correct in the actual file):
def index
# to place all productions on map
#productionsall = Production.all
#geojson = Array.new
#productionsall.each do |p|
#geojson << {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [p.longitude, p.latitude]
},
properties: {
name: p.company,
address: p.full_address,
:'marker-color' => '#00607d',
:'marker-symbol' => 'circle',
:'marker-size' => 'medium'
}
}
end
respond_to do |format|
format.html
format.json { render json: #geojson } # respond with the created JSON object
end
end
Then on the Index page, I've got the map itself and an attempt at a function (borrowed from a Stack Overflow answer) to add a layer:
<div id='map'></div>
<script>
L.mapbox.accessToken = 'xxxxx';
var map = L.mapbox.map('map', 'example.idkeiso')
.setView([41.344,-99.316], 5);
var zipLayer;
$.getJSON('production/index.json', function(data) {
zipLayer = L.mapbox.featureLayer(data);
zipLayer.addTo(map);
});
</script>
The map appears, but without any markers.
Thanks for your help!
Related
I've created a client-side feature (point) and would like to know how to return it's lat and long. I've tried using the queryFeatures method, but have not been able to return anything.
featureLayer
.queryFeatures({
returnGeometry: true,
})
.then(function (results) {
// do something with the resulting graphics
console.log(results.features.geometry.latitude);
});
EDIT: I'm only adding one point that I need to query. It has an initial location, but is moved when the user clicks a new location on the map.
var features = [
{
geometry: {
type: "point",
x: -94.68,
y: 46.72,
},
attributes: {
ObjectID: 1,
},
},
];
var featureLayer = new FeatureLayer({
source: features, // autocast as a Collection of new Graphic()
objectIdField: "ObjectID",
});
map.add(featureLayer);
If you want to query a feature layer that has the features on client side, you need to use the FeatureLayerView methods (ArcGIS API - FeatureLayerView queryFeatures). This methods act on every graphic available to dray on the view, these is what you are looking for.
The query methods of FeatureLayer act on the service, that is not your case.
BTW, if you only need to update the geometry just use applyEdits method of the feature layer (ArcGIS API - FeatureLayer applyEdits).
I have a huge geojson file having around 100K feature's and I want to filter feature's outside the viewport. I tried multiple approach given on:
https://github.com/mapbox/mapbox-gl-js/issues/8350
and
https://gis.stackexchange.com/questions/300292/how-to-load-geojson-features-outside-view-with-mapbox-gl?rq=1
But nothing seems to work.
My Code is:
//Here i have already added empty geojson source
this.instance.addSource('seats_geojson', {
type: 'geojson',
data: {
"type": "FeatureCollection",
"features": []
}
});
//Here i have axios call with await and i am getting all the feature's in console
//console.log(e.data.featuresCollection.features) <--- 100K features
this.instance.once('idle', () => {
//Once i have the reponse ready I am setting source using setData
this.instance.getSource('seats_geojson').setData(e.data.featuresCollection);
//Here i am creating empty layer with above source
this.instance.addLayer({
id: "rowSelectionDup",
type: 'circle',
source: 'seats_geojson',
paint: {
'circle-color': "#4400d9"
},
filter: [
"in", "s", ""
]
});
});
but on doing something like:
this.instance.querySourceFeatures('seats_geojson', {
sourceLayer: 'rowSelectionDup',
filter: ['in', 's', "1C"] //This section "s" 1C is in viewport and i am getting 207 feature's
});
this.instance.querySourceFeatures('seats_geojson', {
sourceLayer: 'rowSelectionDup',
filter: ['in', 's', "7C"]. //This section is outside viewport and result is []
});
Note: this.instance is the map instance(new Map(config....)) of mapbox-gl
Am I doing something wrong here?
or is there any other approach to get feature's from geojson?
Thank you in advance...
As i posted this question on mapbox-gl Github:
https://github.com/mapbox/mapbox-gl-js/issues/9720
As of current mapbox-gl version, there isn't a direct way to fetch the feature's outside of viewport. Hence, as answered by #steve-bennett, I am fetching the geojson url, save as a reference in the javascript oject and applying filter on it is working for me now...
Hope it helps...
I have a geodjango queryset containing several fields but want to use only user_name and location (a point field) which I want to use as a marker in google maps API 3.
Bear with me as I don’t know Javascript and I have a series of questions.
Take this as conceptual brainstorming for a novice:
My SO search suggests that I need to serialize the queryset objects
to JSON. I use the built-in serializer module to convert to JSON.
I think the JSON objects are converted in views.py (let’s
call it json_data). Are these JSON objects stored in the PostGIS database? Wouldn’t that be redundant?
Furthermore, how do I reference them in the map.js (google maps
API 3) javascript file? I want to (import?link?) JSON objects to display them as location markers.
I want to know how to declare and iterate the javascript variable
locations.
For var(i=0;i< locations.length;i++){[
[json_data.user_name, json_data.point],
]
var map = new google.maps.Map(document.getElementById('map'), {
center: new google.maps.LatLng(49.279504, -123.1162),
zoom: 14,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var infowindow = new google.maps.InfoWindow();
var marker, i;
for (i = 0; i < locations.length; i++) {
marker = new google.maps.Marker({
position: new google.maps.LatLng(locations[i][1], locations[i][2]),
map: map,
icon: 'http://maps.google.com/mapfiles/ms/icons/blue-dot.png'
});
google.maps.event.addListener(marker, 'click', (function(marker, i) {
return function() {
infowindow.setContent(locations[i][0]);
infowindow.open(map, marker);
}
})(marker, i));
}
Guide me if I went unnecessarily convoluted way to do a simple task.
TL;DR
No, what you are doing is not redundant and nothing get's written to the database from those answers.
You need to make a getJSON() or similar call to your API's endpoint to access the data.
You can do it on the 2nd step's call and declare it as a list.
What you are thinking is pretty much correct but there is room for improvement (thus the long answer below).
Answer:
Some time ago I read a very good initiation tutorial on building a GIS application with geodjango and google maps. Read it and it should give you a great jump start.
After you read that we will follow a somewhat different way which leaves more room to play around with your front-end (use react for example or whatever comes to mind).
The back-end:
Create a view to retrieve the information you want (user_name, location) as JSON, using the values() queryset method which returns a list of dictionaries.
Since we have to JSONify a list, we will use JsonResponse and we will mark it as unsafe:
from django.http import JsonResponse
def my_view(request):
resp = MyModel.objects.all().values('user_name', 'location')
return JsonResponse(list(resp), safe=False)
Add an endpoint to access that view on urls.py:
urlpatterns = [
...
url(r'^my_endpoint/$', my_view, name='my_endpoint'),
...
]
Now whenever we access the my_endpoint/ we will get a JSON representation of every object's user_name and location in our database which will look like this:
[
{user_name: a_user, location: [lat, lng]},
{user_name: another_user, location: [lat, lng]},
...
]
Moving to the front-end now:
Make a getJSON() or an ajax() or any other type of call to the API and in the same time create a marker list (close to what #MoshFeu suggests):
let map = new google.maps.Map(document.getElementById('map'), {
center: new google.maps.LatLng(49.279504, -123.1162),
zoom: 14,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
let markers = [];
$.getJSON( "my_base_url/my_endpoint", function(data) {
$.each(data, function() {
markers.push(
new google.maps.Marker({
position: {
lat: data['location'][0],
lng: data['location'][1]
},
map: map,
icon: 'http://maps.google.com/mapfiles/ms/icons/blue-dot.png'
})
);
});
});
...
And we are pretty much done!
You don't need to make any special serialization to your data.
You can query the data from any type of front-end you can imagine which gives you designing freedom.
My use-case. I used django.contrib.gis (django.contrib.gis.db.models.PolygonField) and needed to replace default map with Google maps + change default map coordinates, zoom, etc.
TL;DR
I created a new app called gis_addons with custom template and widget to use.
I instructed my model admin (using formfield_overrides) to use my own map widget.
Make sure to add the gis_addons to INSTALLED_APPS.
File: gis_addons/templates/gis_addons/openlayers_googlemaps.html
{% extends "gis/openlayers.html" %}
{% load i18n l10n %}
{% block base_layer %}
var base_layer = new ol.layer.Tile({
source: new ol.source.XYZ({
attributions: [new ol.Attribution({ html: '' })],
maxZoom: 25,
url: "http://mt0.google.com/vt/lyrs=r&hl=en&x={x}&y={y}&z={z}&s=Ga"
})
});
{% endblock %}
{% block options %}var options = {
base_layer: base_layer,
geom_name: '{{ geom_type }}',
id: '{{ id }}',
map_id: '{{ id }}_map',
map_options: map_options,
map_srid: {{ map_srid|unlocalize }},
name: '{{ name }}',
default_lat: 53.2193835,
default_lon: 6.5665018,
default_zoom: 15
};
{% endblock %}
File: gis_addons/widgets.py
from django.contrib.gis.forms.widgets import OpenLayersWidget
class GoogleMapsOpenLayersWidget(OpenLayersWidget):
"""Google Maps OpenLayer widget."""
template_name = 'gis_addons/openlayers_googlemaps.html'
File: my_app/models.py
from django.db import models
from django.contrib.gis.db import models as gis_models
from django.utils.translation import ugettext_lazy as _
class MyModel(models.Model):
# ...
coordinates = gis_models.PolygonField(
verbose_name=_("Geo coordinates"),
)
def __str__(self):
return self.name
File: my_app/admin.py
from django.contrib import admin
from django.contrib.gis.db.models import PolygonField
from gis_addons.widgets import GoogleMapsOpenLayersWidget
from my_app.models import MyModel
#admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
# ...
formfield_overrides = {
PolygonField: {"widget": GoogleMapsOpenLayersWidget}
}
On the application I am working on I am rendering data from columns in my table - for example:
(Model) note.rb
def self.getStudentname
data = []
self.studentnames.each do |type3|
data << self.type3_count(type3)
end
data
end
....
private
def self.studentnames
pluck(:studentname).uniq
end
def self.type3_count(type3)
where(studentname: type3).count
end
(Controller) notes_controller.rb
def dashboard_student
#sData = Note.getStudentname()
#students = Note.studentnames
end
(View) dashboard_student.html.erb
....
xAxis: {
categories: <%=raw #students.to_json %>,
title: {
text: null
},
.....
series: [{
name: 'Number of Notes By Student',
data: <%= #sData %>
}]
As you can see I am getting data from the "studentnames" column in the notes table and rendering on the bar chart in the view which works great.
I am looking to get information for fields in columns so, for example, if I have a column named "greivance" and within that a field labeled "late" how would I get information for all the students that are late keeping the rendering dynamic as above?
Any guidance much appreciated. Thanks. Can provide more code if needed.
I am a novice at RoR and I am looking to use amcharts to create a graph in my Ruby on Rails application (I am using rails -v 3.2.2 and amcharts JavaScript Charts -v 2.6.5) that graphs data from my database. Amcharts no longer uses flash (now they use only Javascript/HTML5) so the integration tutorials that I have found are for the older version of amcharts.
My first question is (high-level understanding question) - do I need to use an XML or .CSV file to load the data from my database into the chart, or can I just use embedded ruby in the javascript?
My second question - does anyone know of any tutorials or example code of how to implement an amchart chart in Ruby on Rails that is pulling its data from the database?
For example:
If I want a pie chart with a country and a value. Example database table:
create_table "countries", :force => true do |t|
t.string "name"
t.integer "litres"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
Here is a hardcoded graph. Can I use embedded ruby to make the code pull the data from the database instead of being hardcoded? Or do I need to make an XML file? Any insight into how to integrate pulling the data from the database into the amcharts javascript code would be much appreciated.
<script type="text/javascript">
var chart;
var legend;
var chartData = [{
country: "Lithuania",
value: 260
}, {
country: "Ireland",
value: 201
}, {
country: "Germany",
value: 65
}, {
country: "Australia",
value: 39
}, {
country: "UK",
value: 19
}, {
country: "Latvia",
value: 10
}];
AmCharts.ready(function () {
// PIE CHART
chart = new AmCharts.AmPieChart();
chart.dataProvider = chartData;
chart.titleField = "country";
chart.valueField = "value";
chart.outlineColor = "#FFFFFF";
chart.outlineAlpha = 0.8;
chart.outlineThickness = 2;
// this makes the chart 3D
chart.depth3D = 15;
chart.angle = 30;
// WRITE
chart.write("chartdiv");
});
</script>
You can quite easily just use Ruby code to pull the data and output the necessary Javascript inside your view, tho this can be painful to build especially with complex data structures.
I would recommend using a separate JSON partial view to output data for the graph using JBuilder. Once you have built up your JSON response, you can just output it inside your graph.
# controller
#data = Country.all
# partial "_data.json.erb"
<%=
Jbuilder.encode do |json|
json.countries #data do |json, country|
json.country country.name
json.value country.litres
end
end
%>
# view
var chartData = <%= render :partial => "data", :format => :json, :locals => #data %>
Also there is an amchart gem I found that might help you, tho looks like it hasn't been touched in more than 3 years https://github.com/ahobson/ambling
I would personally recommend using Highcharts Highcahrts. It has a full api documentation giving you the ability to load data into highcharts. Here's an example of a pie chart that you may well be looking for http://www.highcharts.com/demo/pie-basic. You could try doing something like the following:
Controller
def index
#title = "Country"
#country = Country.all
#data = []
Country.all.each do |country|
countryData = { :CountryName => country.country.to_s,
:values => [] }
['England', 'France', 'Italy', 'Spain','Germany'].each do |NameOfCountry|
end
#data.push(countryData) #Push this data back to the variable countryData.
end
end
View
If you use highcharts you probably will have something basic which is similar to this:
series: [{
type: 'pie',
name: 'Browser share',
data: [
['Firefox', 45.0],
['IE', 26.8],
However the above is just raw static data. Taking some of what I have provided you could well do something like this:
name: '<%= data[ :CountryName] %>',
data: [<% data[ :values ].each do |value| %>
<%= value %>,
<% end %>]
What you can do here is parse in the data of the CountryName as well as the values that you feed in. Hope this helps.
In response to your question you don't necessarily have to use XML or CSV data. What you will usually use is JSON or XML to load your data.
Here is Ruby wrapper for V3 of amCharts: http://rubygems.org/gems/amcharts.rb