How to change shape type in Google Slides? - google-apps-script

I can see that Slides have getShapeType but don't have setShapeType method.
How may we udpate the shape type in Google Slides?
This is possible from the Google Slides context menu, but does not appear to be possible using Google App Script.
Any ideas? Thanks!

From Class Layout you can use getShapes to goto Class Shape where there are a lot methods for working with shapes.

Currently you cannot change the type of an existing Shape with Apps Script. This is not possible even with Slides API (you can change the shape properties in presentations.batchUpdate, but this doesn't include the shape type).
Once a Shape is created, the type is set, and if you want to have a Shape that has another type, you have to create a new one.
There are ways to achieve this, though, even if not so direct and fast:
Workaround #1 (Apps Script methods):
Using Apps Script built-in methods, you could do the following:
Get the properties of the shape you want to update (including position, rotation, scaling, color, etc.), with the corresponding methods (getRotation, getHeight, etc.).
Delete this existing shape via Shape.remove.
Create a new shape with the same properties as the old one, but with a different shape, via Slide.insertShape.
See for example, this sample, in which the position, rotation and dimensions of an existing shape is retrieved, then the shape is removed, and finally a new one is created with the same position, rotation and dimensions of the old one, and a triangular shape:
var left = shape.getLeft();
var top = shape.getTop();
var rotation = shape.getRotation();
var height = shape.getHeight();
var width = shape.getWidth();
shape.remove();
var shapeType = SlidesApp.ShapeType.TRIANGLE;
var newShape = slide.insertShape(shapeType, left, top, width, height);
newShape.setRotation(rotation);
Workaround #2 (Advanced Slides Service):
You could also activate Advanced Slides Service and use Slides API to accomplish the same process. You would have to do the following:
Retrieve the shape properties via presentations.get
Remove the old shape via presentations.batchUpdate, providing a DeleteObjectRequest.
Create a new shape with the properties of the old one and the new shape type via presentations.batchUpdate, providing a CreateShapeRequest.
Note:
I'd suggest you consider filing a Feature Request in this Issue Tracker component explaining the potential usefulness of this desired functionality.
Reference:
presentations.batchUpdate
UpdateShapePropertiesRequest
Advanced Slides Service
Slide.insertShape(shapeType, left, top, width, height)

Related

Set Custom Viewpoint for Forge Model for a 360 kind-of centre location viewpoint

Referring to this article, the viewpoints from a model derivative URL manifest can be obtained. But I want to create a custom viewpoint in the model since I don't have those viewpoints. My requirement is below:
I would like to create a viewpoint such that it is centered at a point(aka the pivot) and I should be able to look around that point in all 360 degrees, while staying at that same point (and preventing the user from moving around the model freely if possible). I have the x,y,z viewpoint.
How can I create the aforementioned viewpoint?
How do I create an upward vector?
How do I get aspect, fov and orthoScale?
Also, I need to rotate the camera view based on event from another component. That component provides me data such as pitch, yaw and roll. How can I use that to change the camera view using the viewer.setView() method? Or is there any other method that needs to be used for changing the view with former parameters?
Details:
I have a nwd file loaded in the forge viewer.
I have the offset information available, if that plays a role in the above requirement.
Regarding the up vector, you can get that via viewer.avigation.getCameraUpVector()
For aspect, fov, and orthoScale, it's similar:
const aspect = viewer.navigation.getCamera().aspect;
cosnt fov = viewer.navigation.getVerticalFov();
const orthoScale = viewer.navigation.getCamera().orthoScale; //!<<< for Orthographic camera mode only

Google Slides - central master template

Does anyone know of a way to have a central master template for google slide presentations that automatically cascades changes down to presentations using it ?
If not automatic then maybe there is something that can be done with google apps script to pull any changes to the master template down to the associated presentations ?
Here is a simple example of what I am trying to do:
Create master template/theme (M1) with layout (L1) with two placeholders and a company logo
Create new presentation (P1) importing theme M1 above using Layout L1
Amend master theme M1 Layout A with new company logo or new placeholder
How do i get this change to propagate to P1 without manually importing the template/theme again ? It would be ideal if P1 could subscribe to changes in M1 but i can't see any option for this so was wondering if I could script something ?
Thanks in advance
Greg
This is not possible in Apps Script right now
There is a feature request for this in the Issue Tracker, go give it a ☆!
https://issuetracker.google.com/issues/129457735
Maybe go and explain your use case for it too.
Possible avenue for workaround
The best workaround I can think of is something along the lines of this script:
function copyStyling() {
// This is a standalone script
let masterID = "1107dQEIAbZ8ipBi0wvU6cdy4OV7N2hURT5fjgOwm_vY";
let childID = "1XvGARRBzXofsjrFJkl8SCmt3tQJ2nkw1n9MG3tr9fhU";
// Master Slide Variables
let masterPresentation = SlidesApp.openById(masterID);
let masterSlide = masterPresentation.getSlides()[0];
let masterElements = masterSlide.getPageElements();
// Get style elements
let masterBackground = masterSlide.getBackground();
let masterSolidFill = masterBackground.getSolidFill().getColor();
// etc
// ...
// Child Slide Variables
let childPresentation = SlidesApp.openById(childID);
let childSlide = childPresentation.getSlides()[0];
let childElements = childSlide.getPageElements();
// Updating the stylings for the page
let childBackground = childSlide.getBackground();
childBackground.setSolidFill(masterSolidFill);
// etc
// ...
// Updating the stylings for each element on the page
masterElements.forEach((element, i) => {
childElements[i].setLeft(element.getLeft());
childElements[i].setTop(element.getTop());
// etc
// ...
});
}
This script works if both Master and Child presentations use the same theme (i.e. the master style sheets)
It works by having a single slide in a "Master presentation" which you modify and the Child presentations also have only a single slide.
It gets style info. This script gets the background of the slide (if its a solid fill) and the top left position of each element.
It then updates the child with this information.
It really depends on how many changes are going to happen to the child presentations. If no elements are going to change, and only limited style characteristics are going to change, then it shouldn't take too long to get a working script together. It would just involve going through the documentation and picking out the attributes you want to update.
If the number of elements are going to change, their positions going to be rearranged, with very different content from the placeholders, then it can get considerably more complex. Then it becomes a function of how many hours you can invest into it! Though hopefully this serves as a good starting point for that.
Ideally to this script would be added the width and height of each element to go along with the top and left position, their rotation, transformation, font, font color, font font style, direction, and minimal support for shapes. With these things I believe you could have quite a powerful tool.
Reference
Apps Script Slides Service

Draw polygon using ngmap

I'm trying to draw polygon on my map using ngmap. The map is displayed correctly as this example is showing, but when I finish the draw, this is what I'm getting:
ng-map.min.js:25 Uncaught TypeError: Cannot read property 'onMapOverlayCompleted' of undefined
I used the same code of the example.
My questions:
1- How can I solve this error?
2- If I want to let the user draw only polygon form, I have tried to set the value of drawingMode to "polygon" but I'm always having the other draw options (circle...). How can let only the polygon draw?
3- How can I let the user clear the drawn polygon?
4- Is it possible to detect the action of putting (drawing) every points. I mean that to draw a line in the polygon for example I need at least two points. Can I detect when the user draw the first point then the second and get its coordinates?
Edit
This is the whole error that I see when I include ng-map.js instead of ng-map.min.js :
Uncaught TypeError: Cannot read property 'onMapOverlayCompleted' of undefined
at index (http://localhost:8080/bower_components/ngmap/build/scripts/ng-map.js:2690:44)
at Array.reduce (native)
at Uw.<anonymous> (http://localhost:8080/bower_components/ngmap/build/scripts/ng-map.js:2691:39)
at P4._.z.trigger (https://maps.google.com/maps/api/js?key=MY_API_KEY&libraries=placeses,visualization,drawing,geometry,places:99:121)
at P4.<anonymous> (https://maps.google.com/maps/api/js?key=MY_API_KEY&libraries=placeses,visualization,drawing,geometry,places:37:81)
at C4._.z.trigger (https://maps.google.com/maps/api/js?key=MY_API_KEY&libraries=placeses,visualization,drawing,geometry,places:99:121)
at C4.<anonymous> (https://maps.google.com/maps/api/js?key=MY_API_KEY&libraries=placeses,visualization,drawing,geometry,places:37:81)
at Object._.z.trigger (https://maps.google.com/maps/api/js?key=MY_API_KEY&libraries=placeses,visualization,drawing,geometry,places:99:121)
at F4 (https://maps.google.com/maps-api-v3/api/js/28/14/drawing_impl.js:7:70)
at G4 (https://maps.google.com/maps-api-v3/api/js/28/14/drawing_impl.js:4:249)
Edit 2
I solved the first question:
My problem was that I'm using routes in the app.js. So the html and the controller are linked in it. To solve the problem, I put $scope.onMapOverlayCompleted instead of
var vm = this;
vm.onMapOverlayCompleted because I don't write the the ng-controller="DrawingManagerCtrl as vm" in my HTML. Then in the HTML I put on-overlaycomplete="onMapOverlayCompleted()" and it works.
I solved also the second question by using:
drawing-control-options="{position: 'TOP_CENTER',drawingModes:['polygon']}"
I'm want now to solve the other problems.
Any help please?
3
As the example you referenced shows, the overlaycomplete callback function receives e (Event) as the first parameter. This OverlayCompleteEvent object contains reference to the drawn overlay at Event.overlay (see docs of the original Google Maps JS API more info, since ng-map is only wrapper of the original API after all, specifically "OverlayCompleteEvent object specification" section).
Using that you can get the reference to the created overlay, store it and then later call .setMap(null) on it to delete it from map. E.g:
$scope.myDrawnOverlays = []; //array where you store drawn overlays
$scope.onMapOverlayCompleted(e){
...
myDrawnOverlays.push(e.overlay); //store reference to the drawn overlay after it's drawn
}
$scope.deleteAllDrawnOverlays(){
for(var i = 0; i < myDrawnOverlays.length; i++){
myDrawnOverlays[i].setMap(null); //remove overlays from map one by one
}
myDrawnOverlays = [];
}
Then whenever you want to delete all overlays, just call the function $scope.deleteAllDrawnOverlays (you can tie it to some button using ng-click or do whatever you want with it).
4
This is NOT possible using google.maps.drawing.DrawingManager which is used in your example. The reason is that DrawingManager doesn't support events for clicking at the map while drawing (see docs again for possible events, section "DrawingManager class").
So except hacky workarounds which could break any time with new api release, the only possible solution is to implement drawing of polygon yourself and not use DrawingManager. Check for example this SO answer, specifically the referenced example (right-clicks on map). They don't use ng-map, but basically what would you have to do is to add on-click="mapClickHandler" to your ng-map, in the mapClickHandler get position where you clicked on map, draw Marker there which would look like a dot and if you have more dots connect them with lines.
This is however too much for one question, you would have to probably read up on ng-map and relevant sections of Google Maps Js Api yourself.

Context menu on canvas

I'm trying to use jQuery context menu to add a context menu on a specific element drawin in a html5 canvas, but I have no idea how i could bind it to that. Any tips?
Shapes drawn on html5 Canvas cannot be referenced
Any specific shape/path you've drawn on canvas is not remembered -- the shapes are just pixels on the canvas. So you can't bind your context menu to the un-referenceable shape.
So...You must manually find your shapes
As a starting point, you need to find your shape in the "sea of pixels".
Finding circular & rectangular shapes with Math
If the shape is rectangular or circular, you can mathematically find if the mouse-click is inside the shape using basic math.
// test if the mouse is inside a circle
// Given the circle's centerpoint and its radius
var dx = mouseX-circleCenterX;
var dy = mouseY-circleCenterY;
var mouseIsInCircle = (dx*dx+dy*dy)<(circleRadius*circleRadius);
// test if the mouse is inside a rectangle
// Given the rectangle's x,y,width & height
var mouseIsInRect=(
mouseX>=rect1.x &&
mouseX<=rect1.x+rect1.width &&
mouseY>=rect1.y &&
mouseY<=rect1.y+rect1.height);
Finding more complex paths with context.isPointInPath
One way to reference paths on the canvas is to put the commands that created the path in an object. When you need to hit-test that path to see if the context menu should deploy, you can redefine the path and use context.isPointInPath. This previous Stackoverflow Q&A shows how to use isPointInPath to test if the mouse is inside your complex path.
A better way with Path2D
Path2D is built into most modern browsers (but not IE) and it allows you to save a reference to your complex path. Then you can have context.isPointInPath use that Path2D definition to hit-test your Path2D.
The context menu itself
Here is a previous Stackoverflow Q&A that shows how to create a custom context menu.
If you want to use the jQuery context menu you will need to dynamically create on-demand menus and configure the menu to show at the desired [x,y] on the canvas. Some assembly is required

geotools draw symbol on overlay

I'm new to Geotools. I am developing a simple application that shows a map and I would like to dynamically place a bitmap or vector symbol on it (for example to show where the current position is, like in navigation systems).
Now, what I've done already is:
showing a map given one or more shapefile.
center the map on the last inserted position.
add shapes to new layers when needed.
What I need to do is to create an overlay with images at given coordinates on the map area (to be clear, I don't want to generate a raster layer on disk, I just want to paint on the screen).
My guess is that I have to somehow directly use the Graphics2D instance inside JMapPane, is that correct? In this case how can I convert from the geographic coordinates to pixel coordinates on the drawing pane? Or are there some geotools functions/classes that I should be looking for?
Thank you.
Answering myself, since I found the solution.
I did it by extending class org.geotools.map.DirectLayer, which provides a way to define a custom layer by extending its draw(Graphics2D, MapContent) method. Graphic elements are placed at the correct coordinates by using the worldToScreen affine transform provided by geotools. For example, to define a Point2D placed at the current position given real-world coordinates:
AffineTransform worldToScreen = viewport.getWorldToScreen();
Point2D worldPoint = new Point2D.Double(currentPosition.x, currentPosition.y);
Point2D screenPoint = worldToScreen.transform(worldPoint, null);
Once the draw() method is defined, just instantiate and add as a layer to your MapContent instance.