Is it possible to use RGBA colors with PlanetaryJS elements? - planetary.js

I'm drawing some points over a planet drawn in planetaryjs, and I'd like to make them semi-transparent using RGBA color.
planet.loadPlugin(function(planet) {
planet.onDraw(function () {
planet.withSavedContext(function (context) {
var verylow = (function () {
var verylow = null;
$.ajax({
'async': false,
'global': false,
'url': 'verylow.json',
'dataType': "json",
'success': function (data) {
verylow = data;
}
});
return verylow;
})();
context.beginPath();
planet.path.context(context)(verylow);
context.fillStyle = 'green';
context.fill();
//context.stroke();
context.closePath();
});
});
});
context.fillStyle will also take hex codes, i.e.
context.fillStyle= '#999999';
but it won't take
context.fillStyle= 'rgba (255, 0, 0, 0.6)';
Is this just a limitation of the way that planetary is designed, or can anyone suggest another way to get any level of transparency?
edit: verylow.json contains a bunch of "Point" objects, FYI.

You need to remove the space after the rgba.

Related

How to zoom to bounds of arcs with deck.gl?

I've created some arcs using deck.gl. When you click on different points/polygons, different arcs appear between countries. When doing this, I want the map to zoom to the bounds of those arcs.
For clarity, here is an example: When clicking on Glasgow, I'd want to zoom to the arc shown (as tightly as possible):
It appears that with WebMercatorViewport, you can call fitBounds
(see: https://deck.gl/docs/api-reference/core/web-mercator-viewport#webmercatorviewport)
It's not clear to me how this gets used, though. I've tried to find examples, but have come up short. How can I add this to what I have?
Here is the code for the arcs:
fetch('countries.json')
.then(res => res.json())
.then(data => {
console.log('data',data)
const inFlowColors = [
[0, 55, 255]
];
const outFlowColors = [
[255, 200, 0]
];
const countyLayer = new deck.GeoJsonLayer({
id: 'geojson',
data: data,
stroked: true,
filled: true,
autoHighlight: true,
lineWidthScale: 20,
lineWidthMinPixels: 1,
pointRadiusMinPixels: 10,
opacity:.5,
getFillColor: () => [0, 0, 0],
getLineColor: () => [0,0,0],
getLineWidth: 1,
onClick: info => updateLayers(info.object),
pickable: true
});
const deckgl = new deck.DeckGL({
mapboxApiAccessToken: 'pk.eyJ1IjoidWJlcmRhdGEiLCJhIjoiY2pudzRtaWloMDAzcTN2bzN1aXdxZHB5bSJ9.2bkj3IiRC8wj3jLThvDGdA',
mapStyle: 'mapbox://styles/mapbox/light-v9',
initialViewState: {
longitude: -19.903283,
latitude: 36.371449,
zoom: 1.5,
maxZoom: 15,
pitch: 0,
bearing: 0
},
controller: true,
layers: []
});
updateLayers(
data.features.find(f => f.properties.name == 'United States' )
);
function updateLayers(selectedFeature) {
const {exports, centroid, top_exports, export_value} = selectedFeature.properties;
const arcs = Object.keys(exports).map(toId => {
const f = data.features[toId];
return {
source: centroid,
target: f.properties.centroid,
value: exports[toId],
top_exports: top_exports[toId],
export_value: export_value[toId]
};
});
arcs.forEach(a => {
a.vol = a.value;
});
const arcLayer = new deck.ArcLayer({
id: 'arc',
data: arcs,
getSourcePosition: d => d.source,
getTargetPosition: d => d.target,
getSourceColor: d => [0, 55, 255],
getTargetColor: d => [255, 200, 0],
getHeight: 0,
getWidth: d => d.vol
});
deckgl.setProps({
layers: [countyLayer, arcLayer]
});
}
});
Here it is as a Plunker:
https://plnkr.co/edit/4L7HUYuQFM19m9rI
I try to make it simple, starting from a raw implementation with ReactJs then try to translate into vanilla.
In ReactJS I will do something like that.
Import LinearInterpolator and WebMercatorViewport from react-map-gl:
import {LinearInterpolator, WebMercatorViewport} from 'react-map-gl';
Then I define an useEffect for viewport:
const [viewport, setViewport] = useState({
latitude: 37.7577,
longitude: -122.4376,
zoom: 11,
bearing: 0,
pitch: 0
});
Then I will define a layer to show:
const layerGeoJson = new GeoJsonLayer({
id: 'geojson',
data: someData,
...
pickable: true,
onClick: onClickGeoJson,
});
Now we need to define onClickGeoJson:
const onClickGeoJson = useCallback((event) => {
const feature = event.features[0];
const [minLng, minLat, maxLng, maxLat] = bbox(feature); // Turf.js
const viewportWebMercator = new WebMercatorViewport(viewport);
const {longitude, latitude, zoom} = viewport.fitBounds([[minLng, minLat], [maxLng, maxLat]], {
padding: 20
});
viewportWebMercator = {
...viewport,
longitude,
latitude,
zoom,
transitionInterpolator: new LinearInterpolator({
around: [event.offsetCenter.x, event.offsetCenter.y]
}),
transitionDuration: 1500,
};
setViewport(viewportWebMercator);
}, []);
First issue: in this way we are fitting on point or polygon clicked, but what you want is fitting arcs. I think the only way to overcome this kind of issue is to add a reference inside your polygon about bounds of arcs. You can precompute bounds for each feature and storage them inside your geojson (the elements clicked), or you can storage just a reference in feature.properties to point another object where you have your bounds (you can also compute them on the fly).
const dataWithComputeBounds = {
'firstPoint': bounds_arc_computed,
'secondPoint': bounds_arc_computed,
....
}
bounds_arc_computed need to be an object
bounds_arc_computed = {
minLng, minLat, maxLng, maxLat,
}
then on onClick function just take the reference
const { minLng, minLat, maxLng, maxLat} = dataWithComputedBounds[event.features[0].properties.reference];
const viewportWebMercator = new WebMercatorViewport(viewport);
...
Now just define our main element:
return (
<DeckGL
layers={[layerGeoJson]}
initialViewState={INITIAL_VIEW_STATE}
controller
>
<StaticMap
reuseMaps
mapStyle={mapStyle}
preventStyleDiffing
mapboxApiAccessToken={YOUR_TOKEN}
/>
</DeckGL>
);
At this point we are pretty close to what you already linked (https://codepen.io/vis-gl/pen/pKvrGP), but you need to use deckgl.setProps() onClick function instead of setViewport to change your viewport.
Does it make sense to you?

Invalid imageTag error from ImageStore.getBase64ForTag

I am trying to get a base64 image from facebook profile picture.
getImageFromFacebook() {
const imageURL = this.props.userInfo.picture;
Image.getSize(imageURL, (width, height) => {
var imageSize = {width, height};
ImageEditor.cropImage(imageURL, imageSize, (imageURI) => {
console.log(imageURI);
ImageStore.getBase64ForTag(imageURI, (base64Data) => {
this.setState({pictureBase64: base64Data});
ImageStore.removeImageForTag(imageURI);
}, (reason) => console.log(reason) )
}, (reason) => console.log(reason) )
}, (reason) => console.log(reason))
}
I am following the steps described in this https://github.com/facebook/react-native/issues/1158:
Use Image.getSize(uri) to get the image dimensions.
Use ImageEditor.cropImage(uri, cropData) to store a copy of the image in the ImageStore (if you pass the width and height you got in step 1) then cropImage won't actually crop the image, although it may still make a copy of it.
Use ImageStore.getBase64ForTag(uri) to get the base64 data of the new image (pass the uri you got from the cropImage function, not the original).
Don't forget to call ImageStore.removeImageForTag(uri) once you're done to delete the copy.
Although the ImageEditor.cropImage returns a valid URI (rct-image-store://0), ImageStore.getBase64ForTag fails with a reason:
code: "ERCTERRORDOMAIN0",
domain: "RCTErrorDomain",
message: "Invalid imageTag: rct-image-store://0"
What am I doing wrong?
Found the error!
imageSize should be set like this:
var imageSize = {
size: {
width,
height
},
offset: {
x: 0,
y: 0,
},
};

How to edit PDF file which is exported with jspdf.min.js?

Don't know if the question is well put, but here is my problem: I managed to export my chart to PDF format using jspdf.min.js through this piece of code:
$("#generate").on("click", function (e) {
html2canvas($("#placeholder").get(0), {
onrendered: function (canvas) {
var imgData = canvas.toDataURL('image/png');
console.log('Report Image URL: ' + imgData);
var doc = new jsPDF('portrait');
doc.addImage(imgData, 'PNG', 10, 10, 190, 95);
doc.text("TEST TEXT");//I tried to add a text in PDF file,but didn't work
doc.save('sample-file.pdf');
}
});
});
Is there a way to add a title of my chart in the PDF file?
Try this example. Is a trick which might help you. Don't forget to update your options:
var options = {
canvas: true,
grid: {
margin: {
top:50
},
}
};

Google Maps KML Layer won't Zoom

I have an embedded Google Map using API V3 but I cannot get it default Zoom to anything other than 1.
My JS in the head is:
var map1;
var src1 = 'https://latitude.google.com/latitude/apps/badge/api?user=8963899225283336226&type=kml';
function initialize1() {
map1 = new google.maps.Map(document.getElementById('map-canvas'), {
zoom: 7,
mapTypeId: google.maps.MapTypeId.TERRAIN
});
loadKmlLayer1(src1, map1);
}
google.maps.event.addDomListener(window, 'load', initialize1);
function loadKmlLayer1(src1, map1) {
var kmlLayer1 = new google.maps.KmlLayer(src1, {
suppressInfoWindows: false,
clickable: true,
preserveViewport: false,
map: map1
});
}
The HTML is just the map-canvas div, nothing else. Looking at some of the threads on here it look like its something to do with detecting the viewport and resetting the bounds.
I found a thread that suggested adding something like:
google.maps.event.addListener(kmlLayer1, 'defaultviewport_changed', function() {
var bounds = kmlLayer1.getDefaultViewport();
map.setCenter(bounds.getCenter());
})
but it made no difference. I'm by no means a JS expert and whilst I mostly understand what is going on in most of the code above, I'm not advanced enough to improvise or even understand where it should be placed.
Thanks Molle.
I enhanced to this and it works:
google.maps.event.addListener(kmlLayer, 'status_changed', function () {
console.log('kml loaded:');
google.maps.event.addListenerOnce(map, 'zoom_changed', function () {
console.log('zoom_changed:');
map.setZoom(7);
map.setCenter(new google.maps.LatLng(0, 0));
});
});
The API will set the viewport to contain all KML-features, what will override the zoom-settings.
Reset the zoom once the zoom has changed(as it does when the KML-Layer has been loaded)
google.maps.event.addListenerOnce(map1, 'zoom_changed', function() {
this.setZoom(7);
})

Reverse Geocoding With Dynamic Form

I've been trying to find a way to use the 'Reverse Geocoding' service with the Latitude and Longitude co-ordinates coming from two text boxes on my HTML form, and I must admit I'm not really sure what I need to do.
I have managed to do this with the 'Geocode' service (see code below), but I just wondered whether someone may be able to point me in the right direction of how I could adapt the 'Geocode' javascript I have to the 'Reverse Geocoging' service.
(function Geocode() {
// This is defining the global variables
var map, geocoder, myMarker;
window.onload = function() {
//This is creating the map with the desired options
var myOptions = {
zoom: 5,
center: new google.maps.LatLng(55.378051,-3.435973),
mapTypeId: google.maps.MapTypeId.ROADMAP,
mapTypeControl: true,
mapTypeControlOptions: {
style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR,
position: google.maps.ControlPosition.BOTTOM
},
navigationControl: true,
navigationControlOptions: {
style: google.maps.NavigationControlStyle.ZOOM_PAN,
position: google.maps.ControlPosition.TOP_RIGHT
},
scaleControl: true,
scaleControlOptions: {
position: google.maps.ControlPosition.BOTTOM_LEFT
}
};
map = new google.maps.Map(document.getElementById('map'), myOptions);
// This is making the link with the 'Search For Location' HTML form
var form = document.getElementById('SearchForLocationForm');
// This is catching the forms submit event
form.onsubmit = function() {
// This is getting the Address from the HTML forms 'Address' text box
var address = document.getElementById('GeocodeAddress').value;
// This is making the Geocoder call
getCoordinates(address);
// This is preventing the form from doing a page submit
return false;
}
}
// This creates the function that will return the coordinates for the address
function getCoordinates(address) {
// This checks to see if there is already a geocoded object. If not, it creates one
if(!geocoder) {
geocoder = new google.maps.Geocoder();
}
// This is creating a GeocoderRequest object
var geocoderRequest = {
address: address
}
// This is making the Geocode request
geocoder.geocode(geocoderRequest, function(results, status) {
// This checks to see if the Status is 'OK 'before proceeding
if (status == google.maps.GeocoderStatus.OK) {
// This centres the map on the returned location
map.setCenter(results[0].geometry.location);
// This creates a new marker and adds it to the map
var myMarker = new google.maps.Marker({
map: map,
zoom: 12,
position: results[0].geometry.location,
draggable:true
});
//This fills out the 'Latitude' and 'Longitude' text boxes on the HTML form
document.getElementById('Latitude').value= results[0].geometry.location.lat();
document.getElementById('Longitude').value= results[0].geometry.location.lng();
//This allows the marker to be draggable and tells the 'Latitude' and 'Longitude' text boxes on the HTML form to update with the new co-ordinates as the marker is dragged
google.maps.event.addListener(
myMarker,
'dragend',
function() {
document.getElementById('Latitude').value = myMarker.position.lat();
document.getElementById('Longitude').value = myMarker.position.lng();
var point = myMarker.getPosition();
map.panTo(point);
}
);
}
}
)
}
})();
UPDATE
Firstly, many thanks for the code you kindly posted and the suggestion to go and have a look at the Google documentation.
From what you suggested, and from what I took from the additional documentation I came up with the following. However, when I click my submit button nothing happens, almost as if there is no command attached to it. I don't receive any error messages and I've checked to make sure that I've linked the code to the correct fieldnames and all seems ok. I just wondered whether it would be at all possible if you, or indeed anyone else, could take a look at it please to tell me where I've gone wrong.
Many thanks and kind regards
(function ReverseGeocode() {
var form, geocoderRequest, latlng, myMarker, point;
window.onload = function() {
//This is creating the map with the desired options
var myOptions = {
zoom: 5,
center: new google.maps.LatLng(55.378051,-3.435973),
mapTypeId: google.maps.MapTypeId.ROADMAP,
mapTypeControl: true,
mapTypeControlOptions: {
style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR,
position: google.maps.ControlPosition.BOTTOM
},
navigationControl: true,
navigationControlOptions: {
style: google.maps.NavigationControlStyle.ZOOM_PAN,
position: google.maps.ControlPosition.TOP_RIGHT
},
scaleControl: true,
scaleControlOptions: {
position: google.maps.ControlPosition.BOTTOM_LEFT
}
};
map = new google.maps.Map(document.getElementById('map'), myOptions);
var latlng = new google.maps.LatLng('Latitude', 'Longitude');
// This is making the Geocode request
geocoder.geocode({'LatLng': latlng}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[1]) {
map.setZoom(11);
var myMarker = new google.maps.Marker({
position: results[0].geometry.location,
map: map
});
//This fills out the 'Address' text boxe on the HTML form
document.getElementById('Address').value= results[0].geometry.location.latlng();
var point = myMarker.getPosition();
map.panTo(point);
}
}
}
)}})
Once you have the latitude and longitude from your form, you do something like this (using your above code as a starting point, for the sake of clarity):
var latlng = new google.maps.LatLng(latitudeFromForm,longitudeFromForm);
// This is creating a GeocoderRequest object
var geocoderRequest = {
'latlng':latlng
}
// This is making the Geocode request
geocoder.geocode(geocoderRequest, function(results, status) {
// This checks to see if the Status is 'OK 'before proceeding
if (status == google.maps.GeocoderStatus.OK) {
// Do stuff with the result here
}
If you haven't read it yet, you may want to read the Reverse Geocoding section of http://code.google.com/apis/maps/documentation/javascript/services.html#ReverseGeocoding.