Select State based on event listener - google-fusion-tables

I am trying to create an interactive map of the US, whereby when a location is clicked on the state that that point lies in will become shaded.
I have created an event listener that will draw the layer upon clicking, but I can't figure out how to have the Fusion Table only grab the state data.
I have tried with ST_INTERSECTS.
Is this possible with the fusion table query? would it be better/more efficient to create a lookup table of my own with polygons?
Any help is greatly appreciated, very new to the fusiontables and google maps api!
thanks
google.maps.event.addListener(map, 'click', function( event ){
var layer = new google.maps.FusionTablesLayer({
query: {
select: 'geometry',
from: '17aT9Ud-YnGiXdXEJUyycH2ocUqreOeKGbzCkUw',
where: 'ST_INTERSECTS('geometry',Circle(event.latLng,5000))'
}
});
layer.setMap(map);
I can confirm that this works, and only selects California.
where: 'ST_INTERSECTS(geometry,CIRCLE(LATLNG(37.3242,-121.9806),1))'

I FIGURED IT OUT! You have to create vars for the event.latLng coords like this:
clickLat = event.latLng.lat();
clickLng = event.latLng.lng();
(I don't declare them as 'var' because I want them to not be persistent, see below)
and then construct the where filter like this:
where: 'ST_INTERSECTS(geometry, CIRCLE(LATLNG(' + clickLat + ',' + clickLng + '),1))'
watch those quotes!
Now, I want each click to clear any previous layer, because I want just one state outlined, this is accomplished by adding a simple check:
if (typeof clickLat !== 'undefined') {
layer.setMap(null)
}
I also don't want this layer to be clickable, because if it is, a huge block of the geometry appears, and I have other information I want displayed anyway, this is accomplished by setting the clickability of the FusionTablesLayer to 'false'.
Here is the complete code, fyi:
google.maps.event.addListener(map, 'click', function( event ){
if (typeof clickLat !== 'undefined') {
layer.setMap(null)
}
clickLat = event.latLng.lat();
clickLng = event.latLng.lng();
layer = new google.maps.FusionTablesLayer({
query: {
select: 'geometry',
from: '17aT9Ud-YnGiXdXEJUyycH2ocUqreOeKGbzCkUw',
where: 'ST_INTERSECTS(geometry, CIRCLE(LATLNG(' + clickLat + ',' + clickLng + '),1))'
},
styles: [{
polygonOptions: {
strokeColor: "#5e3cff",
strokeWeight: 3,
fillColor: "#9d9d9d",
fillOpacity: 0.2,
}
}],
clickable: false
});
layer.setMap(map);
});

Related

JqueryUI autocomplete remove results minLength

I have a problem with jqueryui autocomplete.
I print out the results of the autocomplete in another div like this
$(function () {
var ac = $("#search").autocomplete({
source: "myurl",
search: function (event, ui) {
// clear the existing result set
$('#results').empty();
},
minLength: 3
});
ac.data('ui-autocomplete')._renderItem = function (ul, item) {
return $('<div class="col-md-2">' +
'<div class="thumbail">' +
'' +
'</div>' +
'</div>')
.appendTo($('#results'));
};
});
This works great when I search for something with over 2 characters. But if I press backspace and erase one character, making the query less than minLength, the search method is not called anymore, meaning that the previous results stay in my results div. Is there a way to clear the results when the query is shorter than minLength?
Try binding an event handler like:
$("#search").on('input', function() {
if ($(this).val().length >= 3) return;
$('#results').empty();
});

Can't access KML features

I am loading a KML file locally and I have been able to add it to the map successfully. However, I want to interate over the features and can't seem to get anything to work. My code currently:
var myLayer = new ol.layer.Vector({
source: new ol.source.Vector({
url: '/kml/sample.kml',
format: new ol.format.KML()
})
});
// Iterate over features *NOT WORKING*
myLayer.getSource().forEachFeature(function(e) {
console.log(e);
})
Any pointers on how I can get the forEachFeature to function, or any alternative method, would be great.
The code in your question works fine, except that the features are loaded asynchronously. Most of the time it will first execute forEachFeature, which finds 0 features to loop through and afterwards the features are loaded.
You may find out that a single feature is loaded by listening for the addfeature event of the source and maybe you can make your desired changes there for each feature separately:
var id = 1;
myLayer.getSource().on('addfeature', function (ev_add) {
console.log(ev_add.feature);
ev_add.feature.once('change', function (ev_change) {
console.log(ev_change.target.getId());
});
ev_add.feature.setId(x);
x += 1;
});
If you must wait until all features are loaded, the change event of the layer can help:
myLayer.once('change', function () {
myLayer.getSource().forEachFeature(function (feature) {
console.log(feature);
});
});
Edit: You are right, the addfeature event handler has the event object as parameter. To your question about setting the ID while adding features, I think that this is again a problem of waiting until the changes are done. I made the amendments in the first snippet.
I found a way to get this to work. Not sure if it's the most efficient however:
var featProj = map.getView().getProjection();
var kmlFormat = new ol.format.KML();
var myLayer = new ol.layer.Vector();
var vectorSource = new ol.source.Vector({
loader: function() {
$.ajax( {
url: '/kml/my.kml',
success: function( data ) {
var features = kmlFormat.readFeatures( data, { featureProjection: featProj } );
vectorSource.addFeatures( features );
// iterate over features
vectorSource.forEachFeature( function( feature ) {
//do something
console.log( feature );
});
}
});
},
strategy: ol.loadingstrategy.bbox
});
myLayer.setSource( vectorSource );

jQuery UI Sortable with React.js buggy

I have a sortable list in React which is powered by jQuery UI. When I drag and drop an item in the list, I want to update the array so that the new order of the list is stored there. Then re-render the page with the updated array. i.e. this.setState({data: _todoList});
Currently, when you drag and drop an item, jQuery UI DnD works, but the position of the item in the UI does not change, even though the page re-renders with the updated array. i.e. in the UI, the item reverts to where it used to be in the list, even though the array that defines its placement has updated successfully.
If you drag and drop the item twice, then it moves to the correct position.
// Enable jQuery UI Sortable functionality
$(function() {
$('.bank-entries').sortable({
axis: "y",
containment: "parent",
tolerance: "pointer",
revert: 150,
start: function (event, ui) {
ui.item.indexAtStart = ui.item.index();
},
stop: function (event, ui) {
var data = {
indexStart: ui.item.indexAtStart,
indexStop: ui.item.index(),
accountType: "bank"
};
AppActions.sortIndexes(data);
},
});
});
// This is the array that holds the positions of the list items
var _todoItems = {bank: []};
var AppStore = assign({}, EventEmitter.prototype, {
getTodoItems: function() {
return _todoItems;
},
emitChange: function(change) {
this.emit(change);
},
addChangeListener: function(callback) {
this.on(AppConstants.CHANGE_EVENT, callback);
},
sortTodo: function(todo) {
// Dynamically choose which Account to target
targetClass = '.' + todo.accountType + '-entries';
// Define the account type
var accountType = todo.accountType;
// Loop through the list in the UI and update the arrayIndexes
// of items that have been dragged and dropped to a new location
// newIndex is 0-based, but arrayIndex isn't, hence the crazy math
$(targetClass).children('form').each(function(newIndex) {
var arrayIndex = Number($(this).attr('data-array-index'));
if (newIndex + 1 !== arrayIndex) {
// Update the arrayIndex of the element
_todoItems[accountType][arrayIndex-1].accountData.arrayIndex = newIndex + 1;
}
});
// Sort the array so that updated array items move to their correct positions
_todoItems[accountType].sort(function(a, b){
if (a.accountData.arrayIndex > b.accountData.arrayIndex) {
return 1;
}
if (a.accountData.arrayIndex < b.accountData.arrayIndex) {
return -1;
}
// a must be equal to b
return 0;
});
// Fire an event that re-renders the UI with the new array
AppStore.emitChange(AppConstants.CHANGE_EVENT);
},
}
function getAccounts() {
return { data: AppStore.getTodoItems() }
}
var Account = React.createClass({
getInitialState: function(){
return getAccounts();
},
componentWillMount: function(){
AppStore.addChangeListener(this._onChange);
// Fires action that triggers the initial load
AppActions.loadComponentData();
},
_onChange: function() {
console.log('change event fired');
this.setState(getAccounts());
},
render: function(){
return (
<div className="component-wrapper">
<Bank data={this.state.data} />
</div>
)
}
});
The trick is to call sortable('cancel') in the stop event of the Sortable, then let React update the DOM.
componentDidMount() {
this.domItems = jQuery(React.findDOMNode(this.refs["items"]))
this.domItems.sortable({
stop: (event, ui) => {
// get the array of new index (http://api.jqueryui.com/sortable/#method-toArray)
const reorderedIndexes = this.domItems.sortable('toArray', {attribute: 'data-sortable'})
// cancel the sort so the DOM is untouched
this.domItems.sortable('cancel')
// Update the store and let React update (here, using Flux)
Actions.updateItems(Immutable.List(reorderedIndexes.map( idx => this.state.items.get(Number(idx)))))
}
})
}
The reason jQuery UI Sortable doesn't work with React is because it directly mutates the DOM, which is a big no no in React.
To make it work, you would have to modify jQuery UI Sortable so that you keep the DnD functionality, but when you drop the element, it does not modify the DOM. Instead, it could fire an event which triggers a React render with the new position of the elements.
Since React uses a Virtual DOM, you have to use the function React.findDOMNode() to access an actual DOM element.
I would call the jQuery UI function inside the componentDidMount method of your component because your element has to be already rendered to be accessible.
// You have to add a ref attribute to the element with the '.bank-entries' class
$( React.findDOMNode( this.refs.bank_entries_ref ) ).sortable( /.../ );
Documentation - Working with the browser (everything you need to know is here)
Hope that makes sense and resolves your issue

Drag and update location - how to do this?

I'm trying to follow the information on your wiki, stackExchange, etc to make this work.
The idea is to create a new location for the user. The app displays a map, put a marker on the 0,0 location and then the user drag this marker to any place and the form is updated with the new lat and log. Seems trivial and there's doc in the wiki, but I could not make it work.
I'm using gem version 2.1.2 and Rails 4.1. I had success showing maps, markers, etc, but I cannot make the callback and others functions to work. I'm using this doc to try: https://github.com/apneadiving/Google-Maps-for-Rails/wiki/Javascript-goodies#drop-a-marker-and-update-fields-attribute-in-a-form
Follow my view code:
handler = Gmaps.build('Google');
handler.buildMap({ provider: {}, internal: {id: 'map'}}, function(){
markers = handler.addMarkers(
[
{
"lat": 0,
"lng": 0,
draggable: true
}
]
);
handler.bounds.extendWith(markers);
handler.fitMapToBounds();
// Callback function
var markersArray = [];
var marker = handler.getMap().markers[0];
if (marker) {
// Move existing marker when editing a previously stored location
google.maps.event.addListener(marker.serviceObject, 'dragend', function() {
updateFormLocation(this.getPosition());
});
}
// On click, clear markers, place a new one, update coordinates in the form
google.maps.event.addListener(handler.getMap().serviceObject, 'click', function(event) {
clearOverlays();
placeMarker(event.latLng);
updateFormLocation(event.latLng);
});
});
// Other functions
// Update form attributes with given coordinates
function updateFormLocation(latLng) {
$('#centre_latitude').val(latLng.lat());
$('#centre_longitude').val(latLng.lng());
$('#centre_gmaps_zoom').val(handler.getMap().serviceObject.getZoom());
}
// Add a marker with an open infowindow
function placeMarker(latLng) {
var marker = new google.maps.Marker({
position: latLng,
map: handler.getMap().serviceObject,
draggable: true
});
markersArray.push(marker);
// Set and open infowindow
var infowindow = new google.maps.InfoWindow({
content: '<div class="popup"><h2>Awesome!</h2><p>Drag me and adjust the zoom level.</p>'
});
infowindow.open(handler.getMap().serviceObject, marker);
// Listen to drag & drop
google.maps.event.addListener(marker, 'dragend', function() {
updateFormLocation(this.getPosition());
});
}
// Removes the overlays from the map, including the ones loaded with the map itself
function clearOverlays() {
for (var i = 0; i < markersArray.length; i++ ) {
markersArray[i].setMap(null);
}
markersArray.length = 0;
for (var i = 0; i < handler.getMap().markers.length; i++ ) {
handler.getMap().clearMarker(handler.getMap().markers[i]);
}
}
Then I get an error:
TypeError: undefined is not an object (evaluating 'handler.getMap().markers[0]')
Also, the marker is not draggable...
Is there any complete example of how to do this?
Thanks all!
There are many errors in your code. Hard to explain one by one.
Here is a solution suggestion.
Basically when you use
handler.getMap()
its already a google maps object, so dont do:
handler.getMap().serviceObject
I think I found a solution. Follows the code
handler = Gmaps.build('Google');
handler.buildMap({ provider: {}, internal: {id: 'map'}}, function(){
markers = handler.addMarkers(
[
{
"lat": 0,
"lng": 0,
}
]
,{ draggable: true}
);
handler.bounds.extendWith(markers);
handler.fitMapToBounds();
// Move existing marker
google.maps.event.addListener(markers[0].serviceObject, 'dragend', function() {
updateFormLocation(this.getPosition());
});
});
// Update form attributes with given coordinates
function updateFormLocation(latLng) {
$('#latitude').val(latLng.lat());
$('#longitude').val(latLng.lng());
}
Now the marker is draggable and I can update the correct field in the form.
Any other approaches are welcome.

How to call Modal Dialog from Datatables row - seem to have conflict with Jquery UI

I want to create "CRUD" functions by calling a modal form by clicking on a row in Datatables.
I've been at this for hours traversing through each step of my code and it seems I'm getting a conflict between my JQ-UI and Datatables. I found several examples, including the Datatables example for "live" functions, where you can initialize a table and call a simple jquery function.
I'm using:
code.jquery.com/jquery-1.9.1.js
code.jquery.com/ui/1.10.2/jquery-ui.js
../DataTables-1.9.4/media/js/jquery.dataTables.js
This example will give me the cursor, then makes the table "jump" across the page.
Does anyone have a working example or a fiddle I can experiment with?
function openDialog() {
$("#dialog-modal").dialog({
height: 140,
modal: true
});
}
/* Init DataTables */
$('#example').dataTable();
/* Add events */
$('#example tbody tr').on('click', function () {
$('#example tbody tr').css('cursor', 'pointer');
var sTitle;
var nTds = $('td', this);
var sBrowser = $(nTds[1]).text();
var sGrade = $(nTds[4]).text();
/*
if (sGrade == "A")
sTitle = sBrowser + ' will provide a first class (A) level of CSS support.';
else if (sGrade == "C")
sTitle = sBrowser + ' will provide a core (C) level of CSS support.';
else if (sGrade == "X")
sTitle = sBrowser + ' does not provide CSS support or has a broken implementation. Block CSS.';
else
sTitle = sBrowser + ' will provide an undefined level of CSS support.';
*/
openDialog();
//alert( sTitle )
});
A little sleep and another stab at this yielded a solution that at least solves the Datatable Dialog issue, I'll have to assume that any other issues I was having lies the other add-ins that I included. So to me this is solved.
The answer was 99% in this post - thanks to the author for the great working example.
I modified their link solution, combined with Datatables "live" solution example with variables, and was able to successfully pass data to a working dialog that works with pagination as the previous link explains.
This set up would allow me to create JQuery-UI Modal Forms, pass the ID from mySQL table column, and execute the form that's handing the Server Side PHP CRUD functions I needed.
(I can't take credit for any part of this, other than time spent making sure it worked).
The working example is taken straight from Datatables "live events" example, should be easy to drop in if you remove the sAjaxsource and go with a plain Datatable..
$('#example').dataTable( {
"bProcessing": true,
"bServerSide": true,
"bJQueryUI": true,
"bStateSave": true,
"sPaginationType": "full_numbers",
"sAjaxSource": " /*your data source page here*/ "
} );
/* Add events */
$("body").on("click", "#example tbody tr", function (e) {
e.preventDefault();
var nTds = $('td', this);
//example to show any cell data can be gathered, I used to get my ID from the first coumn in my final code
var sBrowser = $(nTds[1]).text();
var sGrade = $(nTds[4]).text();
var dialogText="The info cell I need was in (col2) as:"+sBrowser+" and in (col5) as:"+sGrade+"" ;
var targetUrl = $(this).attr("href");
$('#table-dialog').dialog({
buttons: {
"Delete": function() {
window.location.href = targetUrl;
},
"Cancel": function() {
$(this).dialog("close");
}
}
});
//simple dialog example here
$('#table-dialog').text(dialogText ).dialog("open");
});

Resources