Bay Bridge Approach
Black and white photo of the approach to the Bay Bridge, San Francisco in the background, low tide in the foreground.
The photograph titled "Bay Bridge Approach" captures a serene yet evocative view, where the iconic structure of the bridge stretches gracefully across the frame. The skyline stands sentinel in the background, softened by a delicate mist that blurs the boundary between the city and the sky. The land meets the water softly at the foreground, where the glistening surface reflects the diffused morning light. A gathering of birds animates the scene, their silhouettes scattered across the wet, reflective sands, bringing life to the tranquil seascape. The low tide exposes textures within the earth, which mirror the ridged clouds lingering softly overhead, creating a harmonious balance between earth and sky.
The use of black and white gives the photograph a timeless quality, abstracting the scene and drawing the viewer's attention to the play of light and shadow. The composition adheres to classic principles of design, with the bridge effectively guiding the viewer’s eye through the image. The inclusion of natural elements like the water, sand, and birds offers a subtle complexity and depth to the simple yet powerful linear architecture. I appreciate the minimalist approach, accentuated by the soft gradient of tones that transition from the foreboding darkness of the foreground to the ethereal brightness of the horizon. However, the image might benefit from a stronger focal point, as the diffuse lighting and monochrome palette can sometimes render the photograph too uniform, reducing the dynamism that might otherwise engage the viewer’s curiosity more intensely.
Related Posts
(Published to the Fediverse as: Bay Bridge Approach #photo #baybridge Photo of the Bay Bridge with San Francisco in the background and low tide in the foreground. Black and White. )
San Francisco
Photo of San Francisco from Grand View Park looking over Golden Gate Park, the Presidio and the Golden Gate Bridge.
The photograph titled "San Francisco" captures a sweeping view over a significant portion of the cityscape, with its vibrant urban fabric sprawling under the soft embrace of a clear blue sky. In the foreground, the architecture houses a blend of modern and historical buildings, their roofs forming a rhythmic pattern of shapes and textures. Dominating the scene is a prominent cathedral with its twin towers, intricately detailed and adding an element of historical grandeur. The middle ground transitions into a lush expanse of greenery, suggesting the presence of one of the city's many parks, providing a refreshing contrast between nature and civilization. In the background, the iconic silhouette of the Golden Gate Bridge subtly peeks through the mist, flanked by rolling hills that shape the distant horizon, creating a sense of endless possibilities.
The style of the photograph is a harmonious blend of urban landscape and architectural portraiture, using a high vantage point to encapsulate the diversity of San Francisco's topography and skyline. The composition is robust, employing the rule of thirds to balance the cityscape with the bridge and hills, drawing the viewer's eye naturally from the foreground to the background. The photograph benefits from clear and vibrant lighting, enhancing details and colors that make the scene lively and inviting. However, the image could benefit from a more focused subject or a leading line to guide the viewer more directly through the composition. While the inclusion of the Golden Gate Bridge is iconic, its subtlety in the frame might leave some viewers desiring a more pronounced presence. Nonetheless, the overall impression is one of a dynamic city juxtaposed against a serene natural backdrop, capturing the unique essence of San Francisco.
Related Posts
- Golden Gate
- Golden Gate Park from Grand View Park
- GGB
- San Francisco from Twin Peaks
- San Francisco from Mount Davidson
(Published to the Fediverse as: San Francisco #photo #sanfrancisco Photo of San Francisco, California from Grand View Park including the Golden Gate Bridge, Presidio and Golden Gate Park. )
Vernal (Spring) Equinox 2018
Springtime in the Northern Hemisphere, Autumn starts south of the Equator. Rendered in Catfood Earth.
(Previously, Previously, Previously, Previously)
Related Posts
Facebook shouldn't own your social graph
"Get News. Not too quickly. Avoid social. — Farhad Manjoo"
It's time to break up the Facebook social media monopoly.
There has been a shift in attitude towards regulation of tech companies recently, according to Axios:
"A majority of Americans are now concerned that the government won't do enough to regulate how U.S. technology companies operate, according to an Axios-SurveyMonkey poll. Across the board, concern about government inaction is up significantly — 15 percentage points — in the past three months."
Roger McNamee recently suggested a subscription model in a Washington Post OpEd:
"Despite a firestorm of criticism, Facebook refuses to make material changes to its business practices. It has also refused to provide substantive data about Russian interference to congressional committees, despite several requests. As a result, we can expect interference in the upcoming midterm elections. Anyone can follow the Russian playbook; many are likely to do so."
I've made the same argument myself:
"I tried Diaspora and App.net but they make Google Plus look lively. Facebook, I would pay you for an ad-free, brand-free experience. Also a ban on text on images."
Facebook is unlikely to switch to an ad-free subscription model without being forced to do it. And if we're going to force them to do something why not make them open up the social graph?
Your social network should be your property and you should be able to move it between providers at will. All social network providers should push your content out to your network regardless of where your friends live and accept content back in the same way. Content may be blocked or altered based on community standards on import but never on export. We should mandate this portability and interoperability via legislation.
This means you can 'live' on Facebook or Google or somewhere new. Social media can become competitive again. You might choose to pay a subscription fee to have a friends only feed (maybe in the order that it was published). You might choose censorship, or you might prefer a platform that can handle breastfeeding. There will be plenty of room for innovation on top of the core network. Facebook will probably be a smaller company. Democracy might last a little longer.
This doesn't solve all the problems with Facebook (and social media in general) but it could be an important first step.
(Photo by Shripal Daphtary on Unsplash)
Related Posts
- I didn't think I'd ever fall for fake news on Facebook
- The Trust Project, Fake News and a Partial Facebook Uninstall
- Facebook Interoperability
- Circles
- Fight Facebook with Email
(Published to the Fediverse as: Facebook shouldn't own your social graph #politics #facebook #fakenews It's time to force Facebook (and others social media providers) to open up the social graph. Via legislation. This won't solve every problem with social media but it's an important first step. )
Book reviews for February 2018
Dark State (Empire Games #2) by Charles Stross
4/5
It's going to be a long, long wait for the third book...
Related Posts
Gun Insurance could pay for Buybacks
Gun buybacks are not a new idea, in fact a mandatory buyback scheme was a big success in Australia. Gun insurance as a means of reducing gun violence isn't either. But what if you combined the two?
Set up a mandatory government run insurance program for all new firearms sales. If you are a 40 year old farmer with a shotgun stored in a gun safe then insurance is pretty cheap. If you are a 19 year old with an assault rifle then it is eyewateringly expensive. The risk that you will do something harmful with the gun is priced into the cost of ownership.
One of the criticisms of gun insurance (and of any gun control measure in general) is that there are so many firearms already in circulation in the US. So take the proceeds of the insurance scheme and use them to buy back guns.
Responsible gun owners are not inconvenienced, we reduce risky firearms sales and slowly draw down the number of uninsured guns in circulation.
Related Posts
- I would do anything to reduce gun violence, but I won't do that (an open letter to Joe Manchin)
- Drones and Gun Control
- California November 2016 Propositions
- California November 2020 Propositions
- Legislative Service
(Published to the Fediverse as: Gun Insurance could pay for Buybacks #politics #guns #insurance A Government run gun insurance program would reduce risky firearm sales and at the same time buy back and destroy excess weapons. It would not infringe on the rights of responsible gun owners. )
Liquid Democracy and united.vote
This November there will be a limited opportunity to put liquid democracy into action. Limited to District 19 of the California Assembly (where I happen to live) and limited to the candidacy of David Ernst (which is a long shot).
Liquid democracy is a referendum on every act, with the difference that you can delegate your vote to someone you trust instead of voting on every issue yourself. At one extreme it's mob rule and at the other (if you delegate your vote to David) it's no different than the current system. The potential magic is where various groups of voters are powerfully represented by someone who is closer to their ideal candidate. If elected David promises to vote based on a liquid democracy implementation at united.vote.
I think functionally liquid democracy shares some problems with proportional representation / transferable vote schemes. If you end up delegating to blocks that represent 40%, 45% and 15% of voters then the 15% block holds the balance of power and gains undue influence. It raises new problems as well. If my elected representative is just doing what my delegate says and I can change my delegate at any time then there aren't really elections any more. So we've replaced 1 representative with N delegates. Delegates are going to raise money to gain and hold onto power and will become beholden to special interests. Are there term limits for delegates? If we end up with a small pool of powerful delegates does it make any difference if I switch my vote to a group of 0.025% of the electorate?
I'm interested in giving it a try though. I like that I can take back control on issues that I particularly care about. I might well vote for David in November.
So what about united.vote?
You have to pay $1 with a credit card to sign up. This is for voter verification and it's not a steep cost but it is a massive conversion barrier. When I signed up today there were just over 1,000 participants. The site is currently oriented around the US Congress rather than the California Assembly. You don't need a password which is nice. Instead you get emailed a one time code each you log in on a new device.
One big missing feature is some sort of directory of prospective delegates. You can delegate to your existing representative (which is pointless) or search for someone you know but really I want to find someone who closely matches my views and who has a good voting record. This isn't possible right now. Until that's fixed you should delegate your vote to me ;)
The voting record is hard to parse as well. For example Kamala Harris, one of my Senators, has an F. For the votes where she has a score it's 100%, 100%, 92% and 15%. Most votes have no liquid democracy opinion attached (is this a 0?). I need to see the math here.
Having said all that my preferred option is legislative service.
(Photo by chuttersnap on Unsplash)
Related Posts
- Legislative Service
- Open Democracy
- California November 2024 Propositions
- California November 2020 Propositions
- Is Sortition Having a Random Moment?
(Published to the Fediverse as: Liquid Democracy and united.vote #politics #liquiddemocracy #legislativeservice Liquid Democracy is coming to the 19th District of the California Assembly this year (maybe). How does it compare to legislative service and other alternatives? )
Export Google Fit Daily Steps, Weight and Distance to a Google Sheet
Google Fit is a great way to keep track of your daily step count without needing to carry a Fitbit or other dedicated tracker. It's not easy to get that data out though, as far as I can tell the only way is Google Takeout which is not made for automation. Luckily there is an API and you can do almost anything with Google Sheets.
If you're looking to export your step count, weight and distance this post has everything you need, just follow the instructions below to get your spreadsheet up and running. This is also a good primer on using OAuth2 with Google Apps Script and should be a decent starting point for a more complex Google Fit integration. If you have any questions or feedback please leave a comment below.
To get started you need a Google Sheet, an apps script project attached to the sheet and a Google API Project that will provide access to the Fitness API. That might sound intimidating but it should only take a few minutes to get everything up and running.
In Google Drive create a new spreadsheet and call it whatever you like. Rename the first tab to 'Metrics'. Enter 'Date' in cell A1, 'Steps' in B1, 'Weight' in C1 and 'Distance' in D1. To grab history as well create another tab called 'History' with the same headers. Next select 'Script editor...' from the Tools menu which will open a new apps script project.
Give the apps script project a name and then select 'Libraries...' from the Resources menu. Next to 'Add a library' enter 1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF and click Add. This will find the Google OAuth2 library. Choose the most recent version (24 at the time of writing) and click Save. Then select 'Project properties' from the File menu and make a note of the Script ID (a long series of letters and numbers).
Open the Google API Console. Create a new project and name it something like 'Google Fit Sheet'. From the Dashboard click Enabled APIs and services and find and select the Fitness API. Then go to Credentials and create an OAuth Client ID. You'll be asked to create a consent screen, the only field you need to enter is the product name (i.e. 'My Fit App'). Then choose Web Application as the application type. You need to set the name and the authorized redirect URL. The redirect URL is https://script.google.com/macros/d/{SCRIPTID}/usercallback replacing {SCRIPTID} with the actual Script ID you made a note of above. After adding this make a note of the Client ID and Client Secret.
Go back to the apps script project and paste the code below into the Code.gs window:
// add your Google API Project OAuth client ID and client secret here | |
var ClientID = ''; | |
var ClientSecret = ''; | |
function onOpen() { | |
var ui = SpreadsheetApp.getUi(); | |
ui.createMenu('Google Fit') | |
.addItem('Authorize if needed (does nothing if already authorized)', 'showSidebar') | |
.addItem('Get Metrics for Yesterday', 'getMetrics') | |
.addItem('Get Metrics for past 60 days', 'getHistory') | |
.addItem('Reset Settings', 'clearProps') | |
.addToUi(); | |
} | |
function getMetrics() { | |
getMetricsForDays(1, 1, 'Metrics'); | |
} | |
function getHistory() { | |
getMetricsForDays(1, 60, 'History'); | |
} | |
// see step count example at https://developers.google.com/fit/scenarios/read-daily-step-total | |
// adapted below to handle multiple metrics (steps, weight, distance), only logged if present for day | |
function getMetricsForDays(fromDaysAgo, toDaysAgo, tabName) { | |
var start = new Date(); | |
start.setHours(0,0,0,0); | |
start.setDate(start.getDate() - toDaysAgo); | |
var end = new Date(); | |
end.setHours(23,59,59,999); | |
end.setDate(end.getDate() - fromDaysAgo); | |
var fitService = getFitService(); | |
var request = { | |
"aggregateBy": [ | |
{ | |
"dataTypeName": "com.google.step_count.delta", | |
"dataSourceId": "derived:com.google.step_count.delta:com.google.android.gms:estimated_steps" | |
}, | |
{ | |
"dataTypeName": "com.google.weight.summary", | |
"dataSourceId": "derived:com.google.weight:com.google.android.gms:merge_weight" | |
}, | |
{ | |
"dataTypeName": "com.google.distance.delta", | |
"dataSourceId": "derived:com.google.distance.delta:com.google.android.gms:merge_distance_delta" | |
} | |
], | |
"bucketByTime": { "durationMillis": 86400000 }, | |
"startTimeMillis": start.getTime(), | |
"endTimeMillis": end.getTime() | |
}; | |
var response = UrlFetchApp.fetch('https://www.googleapis.com/fitness/v1/users/me/dataset:aggregate', { | |
headers: { | |
Authorization: 'Bearer ' + fitService.getAccessToken() | |
}, | |
'method' : 'post', | |
'contentType' : 'application/json', | |
'payload' : JSON.stringify(request, null, 2) | |
}); | |
var json = JSON.parse(response.getContentText()); | |
var ss = SpreadsheetApp.getActiveSpreadsheet(); | |
var sheet = ss.getSheetByName(tabName); | |
for(var b = 0; b < json.bucket.length; b++) { | |
// each bucket in our response should be a day | |
var bucketDate = new Date(parseInt(json.bucket[b].startTimeMillis, 10)); | |
var steps = -1; | |
var weight = -1; | |
var distance = -1; | |
if (json.bucket[b].dataset[0].point.length > 0) { | |
steps = json.bucket[b].dataset[0].point[0].value[0].intVal; | |
} | |
if (json.bucket[b].dataset[1].point.length > 0) { | |
weight = json.bucket[b].dataset[1].point[0].value[0].fpVal; | |
} | |
if (json.bucket[b].dataset[2].point.length > 0) { | |
distance = json.bucket[b].dataset[2].point[0].value[0].fpVal; | |
} | |
sheet.appendRow([bucketDate, | |
steps == -1 ? ' ' : steps, | |
weight == -1 ? ' ' : weight, | |
distance == -1 ? ' ' : distance]); | |
} | |
} | |
// functions below adapted from Google OAuth example at https://github.com/googlesamples/apps-script-oauth2 | |
function getFitService() { | |
// Create a new service with the given name. The name will be used when | |
// persisting the authorized token, so ensure it is unique within the | |
// scope of the property store. | |
return OAuth2.createService('fit') | |
// Set the endpoint URLs, which are the same for all Google services. | |
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth') | |
.setTokenUrl('https://accounts.google.com/o/oauth2/token') | |
// Set the client ID and secret, from the Google Developers Console. | |
.setClientId(ClientID) | |
.setClientSecret(ClientSecret) | |
// Set the name of the callback function in the script referenced | |
// above that should be invoked to complete the OAuth flow. | |
.setCallbackFunction('authCallback') | |
// Set the property store where authorized tokens should be persisted. | |
.setPropertyStore(PropertiesService.getUserProperties()) | |
// Set the scopes to request (space-separated for Google services). | |
// see https://developers.google.com/fit/rest/v1/authorization for a list of Google Fit scopes | |
.setScope('https://www.googleapis.com/auth/fitness.activity.read https://www.googleapis.com/auth/fitness.body.read https://www.googleapis.com/auth/fitness.location.read') | |
// Below are Google-specific OAuth2 parameters. | |
// Sets the login hint, which will prevent the account chooser screen | |
// from being shown to users logged in with multiple accounts. | |
.setParam('login_hint', Session.getActiveUser().getEmail()) | |
// Requests offline access. | |
.setParam('access_type', 'offline') | |
// Forces the approval prompt every time. This is useful for testing, | |
// but not desirable in a production application. | |
//.setParam('approval_prompt', 'force'); | |
} | |
function showSidebar() { | |
var fitService = getFitService(); | |
if (!fitService.hasAccess()) { | |
var authorizationUrl = fitService.getAuthorizationUrl(); | |
var template = HtmlService.createTemplate( | |
'<a href="<?= authorizationUrl ?>" target="_blank">Authorize</a>. ' + | |
'Close this after you have finished.'); | |
template.authorizationUrl = authorizationUrl; | |
var page = template.evaluate(); | |
SpreadsheetApp.getUi().showSidebar(page); | |
} else { | |
// ... | |
} | |
} | |
function authCallback(request) { | |
var fitService = getFitService(); | |
var isAuthorized = fitService.handleCallback(request); | |
if (isAuthorized) { | |
return HtmlService.createHtmlOutput('Success! You can close this tab.'); | |
} else { | |
return HtmlService.createHtmlOutput('Denied. You can close this tab'); | |
} | |
} | |
function clearProps() { | |
PropertiesService.getUserProperties().deleteAllProperties(); | |
} |
Right at the top of the code there are spaces to enter the Client ID and Client Secret from the API Console. Enter these and save the project.
Switch back to your Google Sheet and reload. After reloading there will be a Google Fit menu item. First select Authorize... You'll get a screen to authorize the script and then a sidebar with a link. Click the link to authorize the script to access your Google Fit data. You can then close the sidebar and select Get Metrics for Yesterday from the Google Fit menu. You should see a new row added to the spreadsheet with yesterday's date and fitness data.
The final step is to automate pulling in the data. Go back to the apps script project and select Current project's triggers from the Edit menu. Add a trigger to run getMetrics() as a time driven day timer - I recommend between 5 and 6am. You can also click notifications to add an email alert if anything goes wrong, like your Google Fit authorization expiring (in which case you just need to come back and authorize from the Google Fit menu again.
At this point you're all set. Every day the spreadsheet will automatically update with your step count from the day before. You can add charts, moving averages, export to other systems, pull in your weight or BMI, etc. I want to add a seven day moving average step count to this blog somewhere as a semi-public motivational tool... watch this space.
Note that weight will be blank in the spreadsheet for days with no weight data. Google Fit doesn't return the last known weight, only the known value for days where an update was recorded.
If you are looking to extend this sample to other data types then this API explorer page is very helpful for finding data types that the API documentation doesn't list.
A couple of times working on this script I got my authorization in a bad state and started getting a 400 error response from the API. If this happens run your Google Fit app, click the Profile icon at the bottom and then the Settings icon at the top right. Click Manage connected apps and then disconnect the script from Google Fit. Finally run the Reset Settings option from the menu in the sheet and then authorize again.
I updated this post on Jan 21, 2019 to extend the sample to handle weight and distance as well as steps. I also improved the history function to handle many days in one API call rather than a quick hack I added earlier that pulled a day at a time. I'd recommend using the code above rather than anything included in comments below (at least comments before this update).
More Google Apps Script Projects
- Get an email when your security camera sees something new (Apps Script + Cloud Vision)
- Get an email if your site stops being mobile friendly (no longer available)
- Email Alerts for new Referers in Google Analytics using Apps Script
- Animation of a year of Global Cloud Cover
- Control LIFX WiFi light bulbs from Google Apps Script
- Adding AI to Todoist with Google Apps Script and OpenAI
- How to backup Google Photos to Google Drive automatically after July 2019 with Apps Script
- Using the Todoist API to set a due date on the Alexa integration to-do list (with Apps Script)
- Automate Google PageSpeed Insights and Core Web Vitals Logging with Apps Script
- Using the Azure Monitor REST API from Google Apps Script
- Monitor page index status with Google Sheets, Apps Script and the Google Search Console API
(Published to the Fediverse as: Export Google Fit Daily Steps, Weight and Distance to a Google Sheet #code #software #fit #appsscript #google #sheets #drive Detailed instructions for setting up an automatic daily export of your step count from Google Fit to a Google Sheet. Uses Google Sheets, Apps Script and the Google Fitness API. )