I loaded my data into kepler.gl (https://kepler.gl/) and created a visual I would like to post online by embedding the code into a blogpost.
However, I want the readers/users not to be able to see and access the side panel, but rather only with the main view of the map.
Is there any way to do so or any parameters I can change when exporting the html?
To solve the issue, one must replace the reducers block:
const reducers = (function createReducers(redux, keplerGl) {
return redux.combineReducers({
// mount keplerGl reducer
keplerGl: keplerGl.keplerGlReducer
});
}(Redux, KeplerGl));
With the following:
const reducers = (function createReducers(redux, keplerGl) {
const customizedKeplerGlReducer = keplerGl.keplerGlReducer.initialState({
uiState: {readOnly: true}
});
return redux.combineReducers({
// mount keplerGl reducer
keplerGl: customizedKeplerGlReducer
});
}(Redux, KeplerGl));
And in the end, change the line with addDataToMap to:
store.dispatch(keplerGl.addDataToMap(loadedData, {readOnly: true}));
Related
Is it possible to reference a React App that is running on another server using
<img src="https://www.react_app.com">
The idea is that the React App returns an image string (or similar) like this:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA ...
So that it can be read in a <img src=""> tag?
The main question is what React code simply sends back a request with the string so that it can be read in src=""?
Also is there a timeout for how long an <img src=""> attempts to fetch an image?
React component imports
import React, { useCallback, useEffect, useState, useRef } from 'react'
import classNames from 'classnames'
import { fabric } from 'fabric'
import fabricConfig from './fabricConfig'
import FileUploader from './components/FileUploader'
import ColorPicker from './components/ColorPicker'
import Checkbox from './components/Checkbox'
import Button from './components/Button'
import getRatio from './utils/getRatio'
import getInitialCanvasSize from './utils/getInitialCanvasSize'
import getImageFromURL from './utils/getImageFromURL'
import resizeCanvas from './utils/resizeCanvas'
import removeSelectedElements from './utils/removeSelectedElements'
import getCanvasObjectFilterRGB from './utils/getCanvasObjectFilterRGB'
import setAttributes from './utils/setAttributes'
import { Z, Y, DELETE } from './utils/constants'
Fetch image from URL and automatically make changes to it on load
const imageUrl = "www.something.com/image"
if (imageUrl) {
new Promise(resolve => fabric.loadSVGFromURL(imageUrl, (objects, options) => {
const group = new fabric.Group(objects)
resolve(getRatio(group, canvas))
}))
.then(({ ratio, width, height }) => {
fabric.loadSVGFromURL(imageUrl, (objects, options) => {
try {
objects.forEach(obj => {
setAttributes(obj, {
left: (obj.left * ratio) + ((canvas.width / 2) - ((width * ratio) / 2)),
top: (obj.top * ratio) + ((canvas.height / 2) - ((height * ratio) / 2)),
})
obj.scale(ratio)
// MAKE EDITS TO THE SVG OBJECT HERE
canvas.add(obj)
})
canvas.renderAll()
// HERE I AM TRYING TO SAVE THE CANVAS STATE AND SEND IT BACK TO THE THIRD PARTY WEBSITE USING GET PARAMETERS
var canvasImg = ''
if(urlParams.get("export") === "png"){
canvasImg = canvas.toDataURL("image/png")
} else if (urlParams.get("export") === "pdf") {
canvasImg = canvas.toDataURL("image/pdf")
} else {
onCanvasModified(canvas)
}
} catch(err) {
console.log('Could not retrieve that image')
}
})
})
What you want is a CDN, which serves image assets via a GET request (the img src accepts a string which it uses to fetch (GET) content). In short, a CDN serves the application with assets -- be it images, javascript, CSS or HTML. A React application is designed to update content in place via manipulating a virtual DOM; therefore, expecting it to serve assets across domains is anti-pattern. Instead, you would use a custom server (like express) or a web server (like nginx) to serve static assets.
As a simple example, imgur.com would the React application, while i.imgur.com would be their CDN to serve images like this and s.imgur.com would be their CDN to serve CSS/JS assets like this.
This answer goes into more detail how to do it; HOWEVER, this is only one of many, many ways on how accomplish the above, but the concept is still the same: Making a request to retrieve an image via an img src string.
I hesitate to provide full example code since I have no idea what stack you're working with and what your knowledge/comfort-level is regarding backend services. As such, if you want practice consuming a request that serves images on the frontend, then I'd recommend you start with this API service.
Example
Here's one of many ways to do it: Example Repo
To run the example...
1.) Clone the repo: git clone git#github.com:mattcarlotta/save-canvas-example.git
2.) Install dependencies: yarn or npm i
3.) Run yarn dev or npm dev
4.) Click one of the buttons to either save an image as PNG or as a PDF
The example includes quite a bit of notes, but to provide a brief:
User clicks button. File Ref
Depending on the clicked button, the canvas is converted to a Blob, which is then converted to a File. File Ref
This file is then sent (via POST) to an image microservice running at http://localhost:4000 listening for requests to /upload-file. File Ref
The microservice sees the request and directs to our middleware functions. File Ref
Then it directs it to the /upload-file controller. File Ref
The controller determines if the file upload was valid (in the middleware), if not it throws an error. File Ref
When valid, the file details are generated from req.file (this comes from our multer middleware function), a directory is created and a file is saved to that directory. File Ref
A filepath is then sent back to the client. File Ref
Client receives filepath from image microservice and sets it to state. File Ref
Client then renders a shareable link, a link to view the file, and a preview. File Ref
Results
Save PNG:
Save PDF:
Flow Diagram
I've tried to reproduce the project with minimal features. user can add and interact with rectangle and save the image. upon saving it would go to the server and the image data will be stored in a JSON file.
Here's the link to frontend: https://codesandbox.io/s/so-fabric-client-5bjsf
As you have mentioned, there are two different react apps; I've created two routes, /draw where the user can draw the image and /images where I fetch the images. you can consider these two routes as different react projects since the logic remains the same regardless of their origin.
On the backend side, for demonstration purposes and simplicity, I've used a JSON file and sending all the file content in response when the application wants to display the images. It could become problematic once there are hundreds of images or when you want to search them by the user. so consider using a database or any other method.
here's the backend code:
const express = require("express");
const path = require("path");
const fs = require("fs");
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.static(path.join(__dirname + "/build")));
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.post("/save-image", (req, res) => {
const image = req.body.image
fs.readFile('images.json', function (err, data) {
if(err) {
console.log(err)
res.status(500)
}
var json = JSON.parse(data)
json.push({id: json.length, image})
fs.writeFile("images.json", JSON.stringify(json), (err, result) => {
if(err) console.log(err);
})
})
res.status(200);
})
app.get("/get-images", (req, res) => {
res.json(JSON.parse(fs.readFileSync("./images.json")))
})
app.listen(PORT, () => {
console.log(`up and running on port ${PORT}`);
})
images.json is just a file with [] as its content. the code is pretty much self-explainatory, I've uploaded all the code on GitHub as well -- so-fabric-demo and you can check the demo on Heroku
I need to open a PDF file from BIM 360 Docs on the Viewer selecting a specific page. I'm currently opening the PDF on the Viewer but I don't know how to select a page.
Adding on to my comment, you can get a list of all available viewables when using the onDocumentLoadSucces callback. This callback is triggered after initting the viewer and loading the first Urn(model) into the viewer. You can look into more functionality on bubble nodes in the documentation
Example in angular/typescript:
private onDocumentLoadSucces(viewerDocument: Autodesk.Viewing.Document) {
// Default viewable
let defaultModel = viewerDocument.getRoot().getDefaultGeometry();
// list of all viewables in this model
this.viewables = viewerDocument.getRoot().search({'type':'geometry'});
// We load in the default viewable here but we could choose to load any viewable
// in this.viewables
this.viewer.loadDocumentNode(viewerDocument, defaultModel);
console.log('model changed');
}
I assume you are using the PDF extension to directly load and view PDF. If so, you can tell it the page number in the loadModel call. For example:
viewer.loadModel( pdf, {page:2});
Here's the whole function:
function initializeViewer( pdf ) {
var options = {
env: "Local",
useADP: false
}
Autodesk.Viewing.Initializer(options, () => {
viewer = new Autodesk.Viewing.Private.GuiViewer3D(document.getElementById('viewer3D'));
viewer.setTheme("light-theme");
viewer.start();
if (!pdf) return;
viewer.loadExtension('Autodesk.PDF').then( () => {
viewer.loadModel( pdf, {page:2});
});
});
};
The docs mention this here:
https://forge.autodesk.com/en/docs/viewer/v7/reference/Extensions/PDFExtension/
and there is also an example I used to test from a blog post here:
https://forge.autodesk.com/blog/fast-pdf-viewingmarkup-inside-forge-viewer
hope it helps
I am following a tutorial to resize images via Cloud Functions on upload and am experiencing two major issues which I can't figure out:
1) If a PNG is uploaded, it generates the correctly sized thumbnails, but the preview of them won't load in Firestorage (Loading spinner shows indefinitely). It only shows the image after I click on "Generate new access token" (none of the generated thumbnails have an access token initially).
2) If a JPEG or any other format is uploaded, the MIME type shows as "application/octet-stream". I'm not sure how to extract the extension correctly to put into the filename of the newly generated thumbnails?
export const generateThumbs = functions.storage
.object()
.onFinalize(async object => {
const bucket = gcs.bucket(object.bucket);
const filePath = object.name;
const fileName = filePath.split('/').pop();
const bucketDir = dirname(filePath);
const workingDir = join(tmpdir(), 'thumbs');
const tmpFilePath = join(workingDir, 'source.png');
if (fileName.includes('thumb#') || !object.contentType.includes('image')) {
console.log('exiting function');
return false;
}
// 1. Ensure thumbnail dir exists
await fs.ensureDir(workingDir);
// 2. Download Source File
await bucket.file(filePath).download({
destination: tmpFilePath
});
// 3. Resize the images and define an array of upload promises
const sizes = [64, 128, 256];
const uploadPromises = sizes.map(async size => {
const thumbName = `thumb#${size}_${fileName}`;
const thumbPath = join(workingDir, thumbName);
// Resize source image
await sharp(tmpFilePath)
.resize(size, size)
.toFile(thumbPath);
// Upload to GCS
return bucket.upload(thumbPath, {
destination: join(bucketDir, thumbName)
});
});
// 4. Run the upload operations
await Promise.all(uploadPromises);
// 5. Cleanup remove the tmp/thumbs from the filesystem
return fs.remove(workingDir);
});
Would greatly appreciate any feedback!
I just had the same problem, for unknown reason Firebase's Resize Images on purposely remove the download token from the resized image
to disable deleting Download Access Tokens
goto https://console.cloud.google.com
select Cloud Functions from the left
select ext-storage-resize-images-generateResizedImage
Click EDIT
from Inline Editor goto file FUNCTIONS/LIB/INDEX.JS
Add // before this line (delete metadata.metadata.firebaseStorageDownloadTokens;)
Comment the same line from this file too FUNCTIONS/SRC/INDEX.TS
Press DEPLOY and wait until it finish
note: both original and resized will have the same Token.
I just started using the extension myself. I noticed that I can't access the image preview from the firebase console until I click on "create access token"
I guess that you have to create this token programatically before the image is available.
I hope it helps
November 2020
In connection to #Somebody answer, I can't seem to find ext-storage-resize-images-generateResizedImage in GCP Cloud Functions
The better way to do it, is to reuse the original file's firebaseStorageDownloadTokens
this is how I did mine
functions
.storage
.object()
.onFinalize((object) => {
// some image optimization code here
// get the original file access token
const downloadtoken = object.metadata?.firebaseStorageDownloadTokens;
return bucket.upload(tempLocalFile, {
destination: file,
metadata: {
metadata: {
optimized: true, // other custom flags
firebaseStorageDownloadTokens: downloadtoken, // access token
}
});
});
I upgraded the forge viewer version of my solution to 6.* to utilize the latest released feature "Document browser extension" as it mentions here
This extension doesn't appear for me, please help.
I got it to work after some experimenting.
Here is my workflow in case you still need it.
First, initialize the viewer:
// initialize the viewer
Autodesk.Viewing.Initializer(adOptions, () => {
// when initialized, call loading function
this.loadDocument(encodedUrn);
});
Then, load your document in the function called above:
// load the document from the urn
Autodesk.Viewing.Document.load(
encodedUrn,
this.onDocumentLoadSuccess,
this.onDocumentLoadFailure,
);
In the success callback you can now do the following:
onDocumentLoadSuccess(doc) {
// get the geometries of the document
const geometries = doc.getRoot().search({ type: 'geometry' });
// Choose any of the available geometries
const initGeom = geometries[0];
// and prepare config for the viewer application
const config = {
extensions: ['Autodesk.DocumentBrowser'],
};
// create the viewer application and bind the reference of the viewerContainer to 'this.viewer'
this.viewer = new Autodesk.Viewing.Private.GuiViewer3D(
this.viewerContainer,
config,
);
// start the viewer
this.viewer.start();
// load a node in the fetched document
this.viewer.loadDocumentNode(doc.getRoot().lmvDocument, initGeom);
}
I hope this will make it work for you as well. What helped me was the reference to the loadDocumentNode function in this blog post.
I am looking at Node.JS request and notice that you can use
var request = require('request');
request(...)
But when I try to do something similar like in the module I try
exports = function() {}
it does not work. The only way I know to use is
var request = require('request').request;
request(...)
and
exports.request = function() {}
How can I set the whole export to a function instead of adding a function to the export object?
A hint might be available in the request source code but I am finding it hard to figure out what is going on. Can you help?
You need to overwrite it as
module.exports = function() {}
Merely writing exports = function() {} creates a new local variables called exports and hides the exports variable living in module.exports