Friday 31 March 2023

Chrome print taking too long for page

I have been developing a print/PDF-friendly template for articles on a WordPress website for which I modified the Tufte CSS template.

For some reason, the print preview render (and subsequent print/saving to PDF) is taking forever on Chrome and browsers with Chromium cores such as Edge. The issue does not persist on Firefox and Safari. You can try for yourself on the link here between Chrome/Edge/(Chromium based browser), Safari and Firefox.

I thought it could probably be because of large text increasing the number of pages, or the JavaScript functions, but testing either has not brought any change to Chrome's behaviour. Loading the page on Chrome's "Emulate CSS print mode" does not show any delay in loading either until it goes to print.

There is no way for me to figure out what is Chrome doing while it's rendering the article for print, and it's driving me nuts.

There are a couple JavaScript functions that run on page load to make the content print-friendly for the A4 format. These are mainly to do with handling the interactive iFrames embedded in the post, making them print-friendly.

Check if any of the iFrames has been substituted with a static image for print, if so, replace it with the static image. Else tag and resize each iFrame to fit into print bounds:

var iframe_exists = false;
        if ($('iframe').length > 0) {
            iframe_exists = true;
        }
        $("iframe").each(function(index){
            if(typeof $("p.print-sub-img")[index] !== 'undefined'){
                $(this).replaceWith($("p.print-sub-img")[index]);
            }
            else {
                newDiv = $('<div>').append($(this).clone());
                let adjusted_dims = null;
                adjusted_dims = adjustFrame($(this).width(), $(this).height());
                newDiv = $('<div>').append($(this).clone().removeAttr('style').attr({'style': 'width: '+adjusted_dims[0]+'px; height: '+adjusted_dims[1]+'px;'})).attr({'class': 'print-frame', 'align': 'center', 'id': 'iframe-'+index});
                $(this).unwrap().replaceWith(newDiv);
            }
        });

Check if the static image substitution above has been completed:

// script from https://www.seancdavis.com/posts/wait-until-all-images-loaded/ - thanks sean!

        // Images loaded is zero because we're going to process a new set of images.
        var imagesLoaded = 0;
        // Total images is still the total number of <img> elements on the page.
        var totalImages = $("img").length;

        // Step through each image in the DOM, clone it, attach an onload event
        // listener, then set its source to the source of the original image. When
        // that new image has loaded, fire the imageLoaded() callback.
        $("img").each(function (idx, img) {
            console.log('img load check', img)
            $("<img>").on("load", imageLoaded).attr("src", $(img).attr("src"));
        });

        // Do exactly as we had before -- increment the loaded count and if all are
        // loaded, call the allImagesLoaded() function.
        function imageLoaded() {
            imagesLoaded++;
            if (imagesLoaded == totalImages) {
            allImagesLoaded();
            }
        }

Create a QR code to link to the web version of the article, and trigger the print command:

function allImagesLoaded() {
            console.log('generating qr....')
            if (iframe_exists == true){
                    var qrcode = new QRCode($("#qr-code")[0], {
                    text: "<?php echo get_site_url().'/'.$post_slug ?>",
                    width: 128,
                    height: 128,
                    correctLevel : QRCode.CorrectLevel.H
                });

                $('#qr-banner').show();
            }

        
        window.print();
        console.log("print triggered")
    }
    });


from Chrome print taking too long for page

FFmpeg - RTMP streaming from Node, stream is faster than realtime

My goal is to render a canvas in Node, and stream that canvas to an RTMP server (Twitch ultimately, but testing on a local RTMP server). The standard way to stream to RTMP seems to be ffmpeg, so I'm using that, spawned as a child process from within node. I've tried a bunch of different combinations of techniques and ffmpeg params to get a consistent framerate and a stream at "realtime" speed, but can't figure it out. Here's the paths I've gone down so far

Render canvas and send input in continuous interval

import { createCanvas } from 'canvas';

const canvas = createCanvas(1920, 1080);
const ctx = canvas.getContext('2d');

const fps = 30;
const ffmpeg = spawn('ffmpeg', [
  '-re',
  '-framerate', String(.fps),
  '-r', String(fps),

  '-i', '-',
  
  '-vcodec', 'libx264',
  '-r', String(fps),
  '-s', '1920x1080',
  '-g:v', String(2*fps),
  '-c:a', 'aac',
  '-f', 'flv', 'rtmp://127.0.0.1/live'
]);
ffmpeg.stdout.pipe(process.stdout)
ffmpeg.stderr.pipe(process.stderr)


const send = () => {
  ctx.fillStyle = 'red'
  ctx.fillRect(0, 0, 1920, 1080);
  ctx.font = '100px Arial';
  ctx.fillStyle = 'black'
  ctx.fillText(new Date().toLocaleString(), 500, 500);
  ffmpeg.stdin.write(canvas.toBuffer())
  setImmediate(() => send())
}
send()
Observations
  • Took about 35 seconds for the stream to actually start (I think because of ffmpeg needing some amount of time to analyze the input?)
  • Frame rate extremely below what I set it to, and "speed" also very low, although I'm not 100% sure what this means. example log Frame= 906 fps=3.9 q=29.0 size= 311kB time=00:00:27.83 bitrate= 91.5kbits/s speed=0.119x
  • Stream behavior
    • Takes about a minute to load once opened in VLC
    • Timer on the stream starts about 1 minute behind real time, stays stuck on a single second for 30+ seconds, then shoots up a few seconds quickly, and gets stuck again

I had a hunch here that at least some of the reason for the strange behavior was that rendering the canvas in the same loop that I send input to ffmpeg in was too slow to achieve 30 FPS.

Render canvas in separate interval from ffmpeg input interval

Only render canvas FPS-times per second

Continue sending input to ffmpeg as fast as possible

import { createCanvas } from 'canvas';

const canvas = createCanvas(1920, 1080);
const ctx = canvas.getContext('2d');

let buffer = canvas.toBuffer();

const fps = 30;
const ffmpeg = spawn('ffmpeg', [
  ...same as before
]);

const render = () => {
  ctx.fillStyle = 'red'
  ctx.fillRect(0, 0, 1920, 1080);
  ctx.font = '100px Arial';
  ctx.fillStyle = 'black'
  ctx.fillText(new Date().toLocaleString(), 500, 500);
  buffer = canvas.toBuffer();
  setTimeout(() => render(), 1/fps)
}
render();

const send = () => {
  ffmpeg.stdin.write(buffer)
  setImmediate(() => send())
}
send()
Observations
  • ffmpeg starts streaming almost immediately
  • fps starts out around 16, takes a couple seconds to hit 28, and then ~30 more seconds to hit 30fps. speed much closer to 1x, but not quite all the way. example log frame=15421 fps= 30 q=29.0 size= 4502kB time=00:08:31.66 bitrate= 72.1kbits/s speed=0.994x
  • Stream behavior
    • Takes about 5 seconds to load once opened in VLC
    • Timer stays stuck on the same second for multiple minutes

My hunch here for the stream being stuck on 1 timestamp is that while ffmpeg is sending frames out at 30 frames per second, I'm sending it frames much quicker than that. So in the first 1st of a second of streaming

  1. Canvas renders with timestamp T 30 times
  2. send runs N times where N is likely way higher than 30, sending ffmpeg N frames with the current timestamp
  3. ffmpeg now has N frames with timestamp T on them, but can only send them out 30 per second, so it takes more than 1 second for the timestamp on the screen to change

Only send ffmpeg a frame every 1/FPS second

Same as before, but instead of sending ffmpeg frames as quickly as possible, only send it FPS frames every second.

import { createCanvas } from 'canvas';

const canvas = createCanvas(1920, 1080);
const ctx = canvas.getContext('2d');

let buffer = canvas.toBuffer();

const fps = 30;
const ffmpeg = spawn('ffmpeg', [
  ...same as before
]);

const render = () => {
  ...same as before
}
render();

const send = () => {
  ffmpeg.stdin.write(buffer)
  setTimeout(() => send(), 1/fps)
}
send()
Observations
  • ffmpeg takes a few seconds to start streaming
  • fps starts out high, around 28, and over the next minute or so drops down to 16. Speed drops along with it. example log frame= 1329 fps= 16 q=29.0 size= 463kB time=00:00:41.93 bitrate= 90.5kbits/s speed= 0.5x
  • Stream behavior
    • Takes about 10 seconds to load once opened in VLC
    • Timer increases about twice as fast as expected, then gets hung on one second for a bit, and then starts increasing again at same rate

I'll stop there, but tl;dr I've tried a whole bunch of different combinations of -re, -framerate, -fps_mode, -r ffmpeg args, and some other techniques in the code like continuing to use setImmediate to send frames, but use a date comparison to actually send a frame at an FPS rate. I'm sure there's probably some fundamental video streaming knowledge I'm missing, so I'm just looking for any sort of guidance on how I can get my canvas to stream at a "realtime" speed, or whatever I could be missing here.



from FFmpeg - RTMP streaming from Node, stream is faster than realtime

Rename subfolder in multiple folders by replacing a part of the string

Assuming I have multiple subfolders inside many different folders (**). All the folders are in a main folder called Project1. I want to replace the string name of the folders using substring function.

import glob
import os 
import pandas as pd

paths = glob.glob("CC:/Users/xxx/College/Project1/**/", recursive=True)

Assuming the subfolders are in multiple folders and have a naming convention as follows:

fwjekljfwelj-10-fwefw (the path for this folder is "CC:/Users/xxx/College/Project1/**/wjekljfwelj-10-fwefw/")
kljkgpjrjrel-11-wwref
fwefjkecmuon-12-cfecd
dsfshncrpout-13-lplce

The alphanumeric sequence prior to the - character is meaningless. I want to replace the string preceding the dashed line with the number 20. The new subfolders would thus be:

2010-fwefw
2011-wwref
2012-cfecd
2013-lplce

I can do it individually for each subfolder using str.split('-', 1)[-1] and then append '20' to the name, but I would like to automate the process. I am renaming folder names, not the files themselves.



from Rename subfolder in multiple folders by replacing a part of the string

Should TrustManager trust expired certificate from TrustStore?

I have the following kotlin code, which is executed as android application:

val expiredCertificate: X509Certificate = ...
val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
putCert(keyStore, expiredCertificate)
val trustManagerFactory = TrustManagerFactory.getInstance(algorithm).apply {
    init(keyStore)
}
val trustManager = trustManagerFactory.trustManagers.firstOrNull() as? X509TrustManager
val ssoContext = SSLContext.getInstance("TLS").apply {
    init(null, arrayOf(trustManager), SecureRandom())
}

In short:

  1. I get (no matter how) expired x509-certificate,
  2. put it in KeyStore,
  3. pass the store to SslContext as store of trusted certificates.

As a result, my ssl-engine has trustStore with expired certificate.

Next, I initiate ssl-connection with backend and receive server`s certificate chain with this exact certificate that resides in trust store.

The question is: upon validating the chain, should ssl-engine check expiration of received certificate? On the one hand, the certificate is trusted (resides in trust store thus is trust anchor), on the other hand, it is expired. Should ssl-engine trust the certificate or not?

P.S. The behaviour I actually observe is the following: the certificate is handled with android.security.net.config.RootTrustManager which delegates to com.android.org.conscrypt.TrustManagerImpl which puts my certificate to a variable with self-explanatory name trustAnchors and does not perform any checks so that my expired certificate is believed to be valid. I wonder whether this is bug or feature.



from Should TrustManager trust expired certificate from TrustStore?

Read a file line by line in Pyodide

The code below reads the user-selected input file entirely. This requires a lot of memory for very large (> 10 GB) files. I need to read a file line by line.

How can I read a file in Pyodide one line at a time?


<!doctype html>
<html>
  <head>
      <script src="https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.js"></script>
  </head>
  <body>
    <button>Analyze input</button>
    <script type="text/javascript">
      async function main() {
        // Get the file contents into JS
        const [fileHandle] = await showOpenFilePicker();
        const fileData = await fileHandle.getFile();
        const contents = await fileData.text();

        // Create the Python convert toy function
        let pyodide = await loadPyodide();
        let convert = pyodide.runPython(`
from pyodide.ffi import to_js
def convert(contents):
    return to_js(contents.lower())
convert
      `);

        let result = convert(contents);
        console.log(result);

        const blob = new Blob([result], {type : 'application/text'});

        let url = window.URL.createObjectURL(blob);

        var downloadLink = document.createElement("a");
        downloadLink.href = url;
        downloadLink.text = "Download output";
        downloadLink.download = "out.txt";
        document.body.appendChild(downloadLink);

      }
      const button = document.querySelector('button');
      button.addEventListener('click', main);
    </script>
  </body>
</html>

The code is from this answer to question "Select and read a file from user's filesystem".


Based on the answer by rth, I used the code below. It still has 2 issues:

  • The chunks break some lines into parts, as shown on the example input file, which has 100 chars per line. The console log (below) shows that this is not always the case for chunks (thus, lines in chunks are broken not at the newline).
  • I cannot get the variable result to be written into the output file, which is available for download to the user (see below, where for the example purposes it is replaced by a dummy string 'result').
<!doctype html>
<html>
  <head>
    <script src="https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.js"></script>
  </head>
  <body>
    <button>Analyze input</button>
    <script type="text/javascript">
      async function main() {
          
          // Create the Python convert toy function
          let pyodide = await loadPyodide();
          let convert = pyodide.runPython(`
from pyodide.ffi import to_js
def convert(contents):
    for line in contents.split('\\n'):
        print(len(line))
    return to_js(contents.lower())
convert
      `);
          
          // Get the file contents into JS
          const bytes_func = pyodide.globals.get('bytes');                                               
          
          const [fileHandle] = await showOpenFilePicker();  
          let fh = await fileHandle.getFile()  
          const stream = fh.stream();  
          const reader = stream.getReader();
          // Do a loop until end of file


          while( true ) {
              const { done, value } = await reader.read();
              if( done ) { break; }
              handleChunk( value );
          }
          console.log( "all done" );


          function handleChunk( buf ) {
              console.log( "received a new buffer", buf.byteLength );
              let result = convert(bytes_func(buf).decode('utf-8'));
          }
          
          const blob = new Blob(['result'], {type : 'application/text'});
          
          let url = window.URL.createObjectURL(blob);
          
          var downloadLink = document.createElement("a");
          downloadLink.href = url;
          downloadLink.text = "Download output";
          downloadLink.download = "out.txt";
          document.body.appendChild(downloadLink);
          
      }
      const button = document.querySelector('button');
      button.addEventListener('click', main);
    </script>
  </body>
</html>

Given this input file with 100 characters per line:

perl -le 'for (1..1e5) { print "0" x 100 }' > test_100x1e5.txt

I am getting this console log output, indicating that lines are broken not at the newline:

received a new buffer 65536
648pyodide.asm.js:10 100
pyodide.asm.js:10 88
read_write_bytes_func.html:41 received a new buffer 2031616
pyodide.asm.js:10 12
20114pyodide.asm.js:10 100
pyodide.asm.js:10 89
read_write_bytes_func.html:41 received a new buffer 2097152
pyodide.asm.js:10 11
20763pyodide.asm.js:10 100
pyodide.asm.js:10 77
read_write_bytes_func.html:41 received a new buffer 2097152
pyodide.asm.js:10 23
20763pyodide.asm.js:10 100
pyodide.asm.js:10 65
read_write_bytes_func.html:41 received a new buffer 2097152
pyodide.asm.js:10 35
20763pyodide.asm.js:10 100
pyodide.asm.js:10 53
read_write_bytes_func.html:41 received a new buffer 1711392
pyodide.asm.js:10 47
16944pyodide.asm.js:10 100
pyodide.asm.js:10 0
read_write_bytes_func.html:37 all done

If I change from this:

const blob = new Blob(['result'], {type : 'application/text'});

to that:

const blob = new Blob([result], {type : 'application/text'});

then I get the error:

Uncaught (in promise) ReferenceError: result is not defined
    at HTMLButtonElement.main (read_write_bytes_func.html:45:34)


from Read a file line by line in Pyodide

Read a file line by line in Pyodide

The code below reads the user-selected input file entirely. This requires a lot of memory for very large (> 10 GB) files. I need to read a file line by line.

How can I read a file in Pyodide one line at a time?


<!doctype html>
<html>
  <head>
      <script src="https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.js"></script>
  </head>
  <body>
    <button>Analyze input</button>
    <script type="text/javascript">
      async function main() {
        // Get the file contents into JS
        const [fileHandle] = await showOpenFilePicker();
        const fileData = await fileHandle.getFile();
        const contents = await fileData.text();

        // Create the Python convert toy function
        let pyodide = await loadPyodide();
        let convert = pyodide.runPython(`
from pyodide.ffi import to_js
def convert(contents):
    return to_js(contents.lower())
convert
      `);

        let result = convert(contents);
        console.log(result);

        const blob = new Blob([result], {type : 'application/text'});

        let url = window.URL.createObjectURL(blob);

        var downloadLink = document.createElement("a");
        downloadLink.href = url;
        downloadLink.text = "Download output";
        downloadLink.download = "out.txt";
        document.body.appendChild(downloadLink);

      }
      const button = document.querySelector('button');
      button.addEventListener('click', main);
    </script>
  </body>
</html>

The code is from this answer to question "Select and read a file from user's filesystem".


Based on the answer by rth, I used the code below. It still has 2 issues:

  • The chunks break some lines into parts, as shown on the example input file, which has 100 chars per line. The console log (below) shows that this is not always the case for chunks (thus, lines in chunks are broken not at the newline).
  • I cannot get the variable result to be written into the output file, which is available for download to the user (see below, where for the example purposes it is replaced by a dummy string 'result').
<!doctype html>
<html>
  <head>
    <script src="https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.js"></script>
  </head>
  <body>
    <button>Analyze input</button>
    <script type="text/javascript">
      async function main() {
          
          // Create the Python convert toy function
          let pyodide = await loadPyodide();
          let convert = pyodide.runPython(`
from pyodide.ffi import to_js
def convert(contents):
    for line in contents.split('\\n'):
        print(len(line))
    return to_js(contents.lower())
convert
      `);
          
          // Get the file contents into JS
          const bytes_func = pyodide.globals.get('bytes');                                               
          
          const [fileHandle] = await showOpenFilePicker();  
          let fh = await fileHandle.getFile()  
          const stream = fh.stream();  
          const reader = stream.getReader();
          // Do a loop until end of file


          while( true ) {
              const { done, value } = await reader.read();
              if( done ) { break; }
              handleChunk( value );
          }
          console.log( "all done" );


          function handleChunk( buf ) {
              console.log( "received a new buffer", buf.byteLength );
              let result = convert(bytes_func(buf).decode('utf-8'));
          }
          
          const blob = new Blob(['result'], {type : 'application/text'});
          
          let url = window.URL.createObjectURL(blob);
          
          var downloadLink = document.createElement("a");
          downloadLink.href = url;
          downloadLink.text = "Download output";
          downloadLink.download = "out.txt";
          document.body.appendChild(downloadLink);
          
      }
      const button = document.querySelector('button');
      button.addEventListener('click', main);
    </script>
  </body>
</html>

Given this input file with 100 characters per line:

perl -le 'for (1..1e5) { print "0" x 100 }' > test_100x1e5.txt

I am getting this console log output, indicating that lines are broken not at the newline:

received a new buffer 65536
648pyodide.asm.js:10 100
pyodide.asm.js:10 88
read_write_bytes_func.html:41 received a new buffer 2031616
pyodide.asm.js:10 12
20114pyodide.asm.js:10 100
pyodide.asm.js:10 89
read_write_bytes_func.html:41 received a new buffer 2097152
pyodide.asm.js:10 11
20763pyodide.asm.js:10 100
pyodide.asm.js:10 77
read_write_bytes_func.html:41 received a new buffer 2097152
pyodide.asm.js:10 23
20763pyodide.asm.js:10 100
pyodide.asm.js:10 65
read_write_bytes_func.html:41 received a new buffer 2097152
pyodide.asm.js:10 35
20763pyodide.asm.js:10 100
pyodide.asm.js:10 53
read_write_bytes_func.html:41 received a new buffer 1711392
pyodide.asm.js:10 47
16944pyodide.asm.js:10 100
pyodide.asm.js:10 0
read_write_bytes_func.html:37 all done

If I change from this:

const blob = new Blob(['result'], {type : 'application/text'});

to that:

const blob = new Blob([result], {type : 'application/text'});

then I get the error:

Uncaught (in promise) ReferenceError: result is not defined
    at HTMLButtonElement.main (read_write_bytes_func.html:45:34)


from Read a file line by line in Pyodide

Thursday 30 March 2023

Plot difference between two Plotly hexbin maps

I've seen posts relating to plotting the difference between two hexbin maps in matplotlib. I couldn't find anything executing the same process but for Plotly hexbin map box plots. If I have two separate hexbin subplots (t, y), is it possible to produce a single plot that subtracts the difference between t and y?

import pandas as pd
import plotly.express as px
import plotly.graph_objs as go
import plotly.figure_factory as ff
from plotly.subplots import make_subplots

data = pd.DataFrame({
   'Cat': ['t','y','y','t','t','t','t','y','y','y','t','y'],
   'LAT': [5,6,7,5,6,7,5,6,7,5,6,7],
   'LON': [10,11,12,10,11,12,10,11,12,10,11,12],
   })

data = pd.concat([data]*5)

df_t = data[data['Cat'] == 't']
df_y = data[data['Cat'] == 'y']

fig = make_subplots(
    rows = 2, 
    cols = 1, 
    subplot_titles = ('t', 'y'),
    specs = [[{"type": "choroplethmapbox"}], [{"type": "choroplethmapbox"}]],
    vertical_spacing = 0.05,
    horizontal_spacing = 0.05
    )

fig2 = ff.create_hexbin_mapbox(data_frame=df_t,
                           lat="LAT", lon="LON",
                           nx_hexagon=5,
                           opacity=0.5,
                           labels={"color": "Point Count"},
                           mapbox_style='carto-positron',
                          )

fig3 = ff.create_hexbin_mapbox(data_frame=df_y,
                           lat="LAT", lon="LON",
                           nx_hexagon=5,
                           opacity=0.5,
                           labels={"color": "Point Count"},
                           mapbox_style='carto-positron',
                          )


fig.add_trace(fig2.data[0], row=1,col=1)
fig.update_mapboxes(zoom=4, style='carto-positron')
fig.add_trace(fig3.data[0], row=2,col=1)
fig.update_mapboxes(zoom=4, style='carto-positron')

fig.update_layout(height=600, margin=dict(t=20,b=0,l=0,r=0))
fig.show()

intended output:

The bottom left bin for t has 15 points, while y has 5. So this will total 10. The middle bin has 10 points for both so will result in 0. The top right has 5 for t and 15 for y, coming to -10. But I'll set vmin to 0 to ensure no negative values.

enter image description here



from Plot difference between two Plotly hexbin maps

Multiple callbacks to filter data - dash Plotly

I'm hoping to include multiple callbacks or combine them to filter data. These functions will be used to visualise graphs.

The first callback returns point data if it's within a designated region. It is assigned to a dropdown bar called area-dropdown:. The dropdown bar and callback function returns smaller subsets from the main df. This is accomplished by merging the point data within a specific polygon area.

The additional callback functions are for a scatter chart and bar chart. They filter unique values in Code and Cat.

At present, I've got the callback functions that filter unique values in Code and Cat operational. This is outlined in the 2nd batch of code. If I comment this section out and use the 1st batch of code, the area dropdown callback is functional.

I'm aiming to find a method that combines both these functions together.

import geopandas as gpd
import plotly.express as px
import dash
from dash import dcc, html, Input, Output
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import plotly.express as px
import plotly.graph_objs as go
import plotly.figure_factory as ff
import geopandas as gpd
from itertools import cycle

# point data
gdf_all = gpd.read_file(gpd.datasets.get_path("naturalearth_cities"))

i = iter(['A', 'B', 'C', 'D'])
gdf_all['Cat'] = gdf_all.index.map(dict(zip(gdf_all.index, cycle(i))))

j = iter(['10-20', '20-30', '30-40', '40-50', '60-70'])
gdf_all['Code'] = gdf_all.index.map(dict(zip(gdf_all.index, cycle(j))))

# polygon data
gdf_poly = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
gdf_poly = gdf_poly.drop('name', axis = 1)

gdf_all['LON'] = gdf_all['geometry'].x
gdf_all['LAT'] = gdf_all['geometry'].y

# subset African continent
Afr_gdf_area = gdf_poly[gdf_poly['continent'] == 'Africa'].reset_index(drop = True)

# subset European continent
Eur_gdf_area = gdf_poly[gdf_poly['continent'] == 'Europe'].reset_index(drop = True)

# function to merge point data within selected polygon area
def merge_withinboundary(gdf1, gdf2):

    # spatial join data within larger boundary
    gdf_out = gpd.sjoin(gdf1, gdf2, predicate = 'within', how = 'inner').reset_index(drop = True)

    return gdf_out

gdf_Africa = merge_withinboundary(gdf_all, Afr_gdf_area)
gdf_Europe = merge_withinboundary(gdf_all, Eur_gdf_area)



external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]

app = dash.Dash(__name__, external_stylesheets = external_stylesheets)

nav_bar =  html.Div([
     html.P("area-dropdown:"),
     dcc.Dropdown(
       id = 'data', 
       value = 'data', 
       options = [{'value': 'gdf_all', 'label': 'gdf_all'},
             {'value': 'gdf_Africa', 'label': 'gdf_Africa'},
             {'value': 'gdf_Europe', 'label': 'gdf_Europe'}
             ],
       clearable=False
  ),

    html.Label('Code', style = {'paddingTop': '1rem'}),
    dcc.Checklist(
            id = 'Code',
            options = [
                 {'label': '10-20', 'value': '10-20'},
                 {'label': '20-30', 'value': '20-30'},
                 {'label': '30-40', 'value': '30-40'},
                 {'label': '40-50', 'value': '40-50'},
                 {'label': '60-70', 'value': '60-70'},                        
                 ],
            value = ['10-20', '20-30', '30-40', '40-50', '60-70'],
            style = {'display': 'inline', 'margin-right': '50px'}
        ),

    html.Label('Cat', style = {'paddingTop': '1rem'}),
    dcc.Checklist(
            id = 'Cat',
            options = [
                 {'label': 'A', 'value': 'A'},
                 {'label': 'B', 'value': 'B'},
                 {'label': 'C', 'value': 'C'},
                 {'label': 'D', 'value': 'D'},                     
                 ],
            value = ['A', 'B', 'C', 'D'],
            style = {'display': 'inline', 'margin-right': '50px'}
        ),

    html.Label('Spatial Map', style = {'paddingTop': '1rem'}),
    dcc.RadioItems(['Scatter','Hexbin'],'Scatter', 
                       id = 'maps', 
                       #labelStyle= {"margin":"1rem"}, 
                       style = {'display': 'inline', 'margin-right': '50px'}
                       ),

 ], className = "vstack gap-2 h-50")


app.layout = dbc.Container([
    dbc.Row([
        dbc.Col(html.Div(nav_bar), className = 'bg-light', width=2),
        dbc.Col([
            dbc.Row([
                dbc.Col(dcc.Graph(id = 'spatial-chart'))
            ]),
            dbc.Row([
                dbc.Col(dcc.Graph(id = 'bar-chart'))
            ]),
        ], width = 5),
        dbc.Col([
        ], width = 5),
    ])
], fluid = True)


df = gdf_all

#================  1st   =======================
# function to return selected df for plotting
#@app.callback(Output('spatial-chart', 'figure'),
#              Output('bar-chart', 'figure'),
#              Input('data', 'value'),
#              prevent_initial_call=True)

# function to return df using smaller areas
#def update_dataset(dropdown_selection):

#    if dropdown_selection == 'gdf_Africa':
#        gdf = gdf_Africa
#        zoom = 2

#    elif dropdown_selection == 'gdf_Europe':
#        gdf = gdf_Europe
#        zoom = 2

#    else:
#        gdf = gdf_all
#        zoom = 0

#    scatter_subset = px.scatter_mapbox(data_frame = gdf, 
#        lat = 'LAT', 
#        lon = 'LON',
#        zoom = zoom,
#        mapbox_style = 'carto-positron', 
#       )
#    count = gdf['name'].value_counts()

#    bar_subset = px.bar(x = count.index, 
#                y = count.values, 
#               color = count.index, 
#                ) 

#    return scatter_subset, bar_subset
#=============================================


#================  2nd   =======================
# function to filter unique Cat/Code for bar chart
@app.callback(
    [Output('bar-chart', 'figure'),
    ],
    [Input('Cat','value'), 
     Input('Code','value'),
     ]
     )     

def date_chart(cat, code):

    dff = df[df['Cat'].isin(cat)]
    dff = dff[dff['Code'].isin(code)]
    count = dff['Cat'].value_counts()

    data = px.bar(x = count.index, 
                 y = count.values,
                 color = count.index, 
                 )

    fig = [go.Figure(data = data)]

    return fig

# function to filter unique Cat/Code for scatter
@app.callback(
    [Output('spatial-chart', 'figure'),
     ],
    [Input('Cat','value'), 
     Input('Code','value'),
     Input("maps", "value"),
     ])     

def scatter_chart(cat, code, maps):

    if maps == 'Scatter':
    
        dff = df[df['Cat'].isin(cat)]
        dff = dff[dff['Code'].isin(code)]

        data = px.scatter_mapbox(data_frame = dff, 
                                   lat = 'LAT', 
                                   lon = 'LON',
                                   color = 'Cat',
                                   opacity = 0.5,
                                   zoom = 1,
                                   mapbox_style = 'carto-positron', 
                                   hover_name = 'Cat',
                                   ) 

        fig = [go.Figure(data = data)]


    elif maps == 'Hexbin':

        dff = df[df['Cat'].isin(cat)]
        dff = dff[dff['Code'].isin(code)]

        data = ff.create_hexbin_mapbox(data_frame = dff, 
                                      lat = "LAT", 
                                      lon = "LON",
                                      nx_hexagon = 100,
                                      min_count = 1,
         )
    

        fig = [go.Figure(data = data)]

    return fig
#=============================================


if __name__ == '__main__':
    app.run_server(debug=True, port = 8051)


from Multiple callbacks to filter data - dash Plotly

Volley throws AuthFailureError

I am using Volley to upload pictures from Android application to server. In case when I have more than 3 pictures for upload, Volley throws AuthFailureError, even though I don't use any kind of authentication. For each picture, I am using separate request, which is sent when I receive response for previous one. Here is a code. Does anyone know what is the problem? Or shall I simply switch to Retrofit? Thanks!

private void sendRequest(String control){

    Toast.makeText(MainReviewActivity.this, control, Toast.LENGTH_LONG).show();

    //Toast.makeText(AfterLoginActivity.this, control, Toast.LENGTH_LONG).show();

    StringRequest stringRequest = new StringRequest(Request.Method.POST, UploadUrl, new Response.Listener<String>() {
        @Override
        public void onResponse(String response) {
            try {
                waiting_search_results = 0;
                JSONObject jsonObject = new JSONObject(response);
                String Response = jsonObject.getString("response");
                if (control.equals("review_page_restaurant_search") || control.equals("review_page_meal_search")){
                    prepareSearchResultDataFromServer(control, Response);
                } else{
                    if (control.equals("review_page_perform_review")){
                        if (Response.equals("Success")){
                            // check if there are pics to send
                            Log.d("debug:", "Perform review - positive resp received");
                            enablePicSending = 1;
                        } else{
                            loadingDialog.dismissDialog();
                            Toast.makeText(MainReviewActivity.this, "Greška u slanju ocene, pokušajte ponovo", Toast.LENGTH_LONG).show();
                        }
                    } else{
                        // control equals "review_page_upload_pic"
                        Log.d("debug:", "Uploading pic - resp received");
                        waitingPicSendingResp = 0;
                    }
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(final VolleyError error) {
            loadingDialog.dismissDialog();
            Log.d("debug:", "Error occurred: " + error);
            //Bundle bundle = new Bundle();
            //bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, "string");
            //bundle.putString(FirebaseAnalytics.Param.CONTENT, "Volley failed during: " + control + ", reason: " + error);
            //mFirebaseAnalytics.logEvent("Sending_failed", bundle);
            Toast.makeText(MainReviewActivity.this, error.toString(), Toast.LENGTH_LONG).show();
            if (control.equals("review_page_upload_pic")){
                Toast.makeText(MainReviewActivity.this, "Slanje slika nije uspelo, pokušajte ponovo", Toast.LENGTH_LONG).show();
            } else{
                if (control.equals("review_page_perform_review")){
                    Toast.makeText(MainReviewActivity.this, "Slanje ocene nije uspelo, pokušajte ponovo", Toast.LENGTH_LONG).show();
                } else{
                    Toast.makeText(MainReviewActivity.this, "Greška u komunikaciji", Toast.LENGTH_LONG).show();
                }
            }
            finish();
        }
    })

    {
        @Override
        protected Map<String, String> getParams() throws AuthFailureError {

            Map<String, String> params = new HashMap<>();

            params.put("control", control);
            params.put("city", selectedCity);
            if (control.equals("review_page_restaurant_search")){
                params.put("search_word", search_word);
            } else{
                if (control.equals("review_page_meal_search")){
                    params.put("search_word", search_word);
                    params.put("restaurant", selectedRestaurant);
                } else{
                    if (control.equals("review_page_perform_review")){
                        params.put("reviewer_pic", reviewer_pic);
                        params.put("reviewer_name", person_name);
                        params.put("reviewer_nick", reviewer_nick);
                        params.put("review_date", date);
                        params.put("restaurant", selectedRestaurant);
                        params.put("ambient", ambientGrade);
                        params.put("quick", quickGrade);
                        params.put("ratio", ratioGrade);
                        params.put("personnel", personnelGrade);
                        params.put("restaurant_comment", restaurantComment);
                        params.put("meal", selectedMeal);
                        params.put("amount", amountGrade);
                        params.put("taste", tasteGrade);
                        params.put("price", priceGrade);
                        params.put("meal_comment", mealComment);
                    } else{
                        if (control.equals("review_page_upload_pic")){
                            Log.d("debug:", "Triggering pic sending: " + picType);
                            Log.d("debug:", "Restaurant: " + selectedRestaurant);
                            Log.d("debug:", "Meal: " + selectedMeal);
                            //Bundle bundle = new Bundle();
                            //bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, "string");
                            //bundle.putString(FirebaseAnalytics.Param.CONTENT, "Triggering pic sending: " + picType + ", restaurant: " + selectedRestaurant + ", meal: " + selectedMeal);
                            //mFirebaseAnalytics.logEvent("Pic_sending", bundle);
                            params.put("type", picType);
                            params.put("pic", pic);
                            params.put("restaurant", selectedRestaurant);
                            params.put("meal", selectedMeal);
                        } else{
                            // should not happen
                        }
                    }
                }
            }

            return params;
        }

        @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            HashMap<String, String> headers = new HashMap<>();
            headers.put("User-Agent", "Mozilla/5.0");

            return headers;
        }

        @Override
        public String getBodyContentType() {
            return "application/x-www-form-urlencoded";
        }
    };

    RequestQueue requestQueue = Volley.newRequestQueue(MainReviewActivity.this);
    requestQueue.add(stringRequest);
}

EDIT I read more about Authentication failure and I noticed in documentation that when that happens, server shall provide WWW-Authenticate response header, so I tried to read it with parseNetworkResponse function. In the moment of Toasting (i.e. the moment after parsing the latest network response), my observations are following: WWW-Authenticate header is missing, some pictures are not uploaded to server, server provided response 200, but Volley called onErrorResponse method (from which I am Toasting). How is it possible to receive response 200, but AuthFailureError is detected in OnErrorResponse method? New code, with parsing network response and debug Toasts:

private void sendRequest(String control){

    //Toast.makeText(MainReviewActivity.this, control, Toast.LENGTH_LONG).show();

    //Toast.makeText(AfterLoginActivity.this, control, Toast.LENGTH_LONG).show();

    StringRequest stringRequest = new StringRequest(Request.Method.POST, UploadUrl, new Response.Listener<String>() {
        @Override
        public void onResponse(String response) {
            try {
                waiting_search_results = 0;
                JSONObject jsonObject = new JSONObject(response);
                String Response = jsonObject.getString("response");
                if (control.equals("review_page_restaurant_search") || control.equals("review_page_meal_search")){
                    prepareSearchResultDataFromServer(control, Response);
                } else{
                    if (control.equals("review_page_perform_review")){
                        if (Response.equals("Success")){
                            // check if there are pics to send
                            Log.d("debug:", "Perform review - positive resp received");
                            enablePicSending = 1;
                        } else{
                            loadingDialog.dismissDialog();
                            Toast.makeText(MainReviewActivity.this, "Greška u slanju ocene, pokušajte ponovo", Toast.LENGTH_LONG).show();
                        }
                    } else{
                        // control equals "review_page_upload_pic"
                        success_count++;
                        //Toast.makeText(MainReviewActivity.this, Response, Toast.LENGTH_LONG).show();
                        Log.d("debug:", "Uploading pic - resp received");
                        waitingPicSendingResp = 0;
                    }
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(final VolleyError error) {
            loadingDialog.dismissDialog();
            Log.d("debug:", "Error occurred: " + error);
            error_count++;
            Toast.makeText(MainReviewActivity.this, "Parse count: " + String.valueOf(parse_count), Toast.LENGTH_LONG).show();
            Toast.makeText(MainReviewActivity.this, "Error count: " + String.valueOf(error_count), Toast.LENGTH_LONG).show();
            Toast.makeText(MainReviewActivity.this, "Success count: " + String.valueOf(success_count), Toast.LENGTH_LONG).show();
            for (int i = 0; i < names.length; i++){
                Toast.makeText(MainReviewActivity.this, "Name: " + names[i] + " Value: " + values[i], Toast.LENGTH_LONG).show();
            }
            //Toast.makeText(MainReviewActivity.this, "Success: " + String.valueOf(success_count) + " Error: " + String.valueOf(error_count) + "Parse: " + String.valueOf(parse_count), Toast.LENGTH_LONG).show();
            //Toast.makeText(MainReviewActivity.this, error.getCause().toString(), Toast.LENGTH_LONG).show();
            //Toast.makeText(MainReviewActivity.this, error.getCause().getMessage(), Toast.LENGTH_LONG).show();
            //Toast.makeText(MainReviewActivity.this, error.getLocalizedMessage(), Toast.LENGTH_LONG).show();
            //Bundle bundle = new Bundle();
            //bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, "string");
            //bundle.putString(FirebaseAnalytics.Param.CONTENT, "Volley failed during: " + control + ", reason: " + error);
            //mFirebaseAnalytics.logEvent("Sending_failed", bundle);
            //Toast.makeText(MainReviewActivity.this, error.toString(), Toast.LENGTH_LONG).show();
            if (control.equals("review_page_upload_pic")){
                Toast.makeText(MainReviewActivity.this, "Slanje pojedinih slika nije uspelo, ali je ocena poslata uspešno", Toast.LENGTH_LONG).show();
            } else{
                if (control.equals("review_page_perform_review")){
                    Toast.makeText(MainReviewActivity.this, "Slanje ocene nije uspelo, pokušajte ponovo", Toast.LENGTH_LONG).show();
                } else{
                    Toast.makeText(MainReviewActivity.this, "Greška u komunikaciji", Toast.LENGTH_LONG).show();
                }
            }
            finish();
        }
    })

    {
        @Override
        protected Map<String, String> getParams() throws AuthFailureError {

            Map<String, String> params = new HashMap<>();

            params.put("control", control);
            params.put("city", selectedCity);
            if (control.equals("review_page_restaurant_search")){
                params.put("search_word", search_word);
            } else{
                if (control.equals("review_page_meal_search")){
                    params.put("search_word", search_word);
                    params.put("restaurant", selectedRestaurant);
                } else{
                    if (control.equals("review_page_perform_review")){
                        params.put("reviewer_pic", reviewer_pic);
                        params.put("reviewer_name", person_name);
                        params.put("reviewer_nick", reviewer_nick);
                        params.put("review_date", date);
                        params.put("restaurant", selectedRestaurant);
                        params.put("ambient", ambientGrade);
                        params.put("quick", quickGrade);
                        params.put("ratio", ratioGrade);
                        params.put("personnel", personnelGrade);
                        params.put("restaurant_comment", restaurantComment);
                        params.put("meal", selectedMeal);
                        params.put("amount", amountGrade);
                        params.put("taste", tasteGrade);
                        params.put("price", priceGrade);
                        params.put("meal_comment", mealComment);
                    } else{
                        if (control.equals("review_page_upload_pic")){
                            Log.d("debug:", "Triggering pic sending: " + picType);
                            Log.d("debug:", "Restaurant: " + selectedRestaurant);
                            Log.d("debug:", "Meal: " + selectedMeal);
                            //Bundle bundle = new Bundle();
                            //bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, "string");
                            //bundle.putString(FirebaseAnalytics.Param.CONTENT, "Triggering pic sending: " + picType + ", restaurant: " + selectedRestaurant + ", meal: " + selectedMeal);
                            //mFirebaseAnalytics.logEvent("Pic_sending", bundle);
                            params.put("type", picType);
                            params.put("pic", pic);
                            params.put("restaurant", selectedRestaurant);
                            params.put("meal", selectedMeal);
                        } else{
                            // should not happen
                        }
                    }
                }
            }

            return params;
        }

        @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            HashMap<String, String> headers = new HashMap<>();
            headers.put("User-Agent", "Mozilla/5.0");
            headers.put("Content-Type", "application/x-www-form-urlencoded");

            return headers;
        }

        @Override
        public String getBodyContentType()
        {
            return "application/x-www-form-urlencoded";
        }

        @Override
        protected Response<String> parseNetworkResponse(NetworkResponse response) {

            parse_count++;

            names = new String[response.allHeaders.size()];
            values = new String[response.allHeaders.size()];

            for (int i = 0; i < response.allHeaders.size(); i++){
                names[i] = response.allHeaders.get(i).getName();
                values[i] = response.allHeaders.get(i).getValue();
                Log.d("debug:", "parse_count" + parse_count);
                Log.d("debug:", "names" + i + ": " + names[i]);
                Log.d("debug:", "values" + i + ": " + values[i]);
            }

            return super.parseNetworkResponse(response);
        }
    };

    RequestQueue requestQueue = Volley.newRequestQueue(MainReviewActivity.this);
    requestQueue.add(stringRequest);
}


from Volley throws AuthFailureError

leafletjs page jumps on layer click

I have a leaflet map with group layers positioned outside of it. When I scroll past the map and click on one of the checkboxes, the page jumps to the top of the map. Why would that happen?

Here is the JavaScript:

var map = L.map('map', {
    center: [51.501617,-0.018582],
    zoom: 14
});

L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
    maxZoom: 18,
    id: 'jeffceriello.nd7obhnh',
    accessToken: 'pk.eyJ1IjoiamVmZmNlcmllbGxvIiwiYSI6Ikhrakxrd00ifQ.SlVngzIXeS5UPC8UGmy1OA'
}).addTo(map);

map.scrollWheelZoom.disable();

// marker
var churchill = L.icon({
    iconUrl: '/img/pin.png',
    shadowUrl: '',

    iconSize:     [43, 64], // size of the icon
    shadowSize:   [0, 0], // size of the shadow
    iconAnchor:   [21, 64], // point of the icon which will correspond to marker's location
    shadowAnchor: [0, 0],  // the same for the shadow
    popupAnchor:  [0, 0] // point from which the popup should open relative to the iconAnchor
});

L.marker([51.503754,-0.014841], {icon: churchill}).addTo(map);

var geojson = {
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [-0.014782, 51.504711]
             },
            "properties": {
                "title": "Barclays Bank",
                markerColor: "#6D8AAA"
            }
        },{
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [-0.014618, 51.504648]
            },
            "properties": {
                "title": "Idea Store",
                markerColor: "#6D8AAA"
            }
        },{
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [-0.014532, 51.504556]
            },
            "properties": {
                "title": "James Shoe Care",
                markerColor: "#6D8AAA"
            }
        }
]};

shopping = L.geoJson(geojson, {
    style: function(feature) {
        return {color: feature.properties.markerColor};
    },
    pointToLayer: function(feature, latlng) {
        return new L.CircleMarker(latlng, {radius: 8, fillOpacity: 1, stroke: false});
    },
    onEachFeature: function (feature, layer) {
        layer.bindPopup(feature.properties.title);
    }
});

map.addLayer(shopping);

var geojson2 = {
    "type": "FeatureCollection",
    "features": [
    {
        "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": [-0.014546, 51.504319]
        },
        "properties": {
            "title": "Birleys",
            "markerColor": "#6D8AAA"
        }
    }
]};

food = L.geoJson(geojson2, {
    style: function(feature) {
        return {color: feature.properties.markerColor};
    },
    pointToLayer: function(feature, latlng) {
        return new L.CircleMarker(latlng, {radius: 8, fillOpacity: 1, stroke: false});
    },
    onEachFeature: function (feature, layer) {
        layer.bindPopup(feature.properties.title);
    }
});

//map.addLayer(food);

var overlayMaps = {
    "Shopping": shopping,
    "Food & Drink": food
};

var control = L.control.layers(overlayMaps, null, {collapsed: false});
control.addTo(map);
control._container.remove();

document.getElementById('layers').appendChild(control.onAdd(map));

And here is a JSFiddle link



from leafletjs page jumps on layer click

Wednesday 29 March 2023

D3 force graph - zoom to node

I am trying to establish a 'clickToZoom' function. On node click the view should focus the clicked node. The event.transform object returns {k, x, y}. So far I thought I can receive those values from the clicked node and set the svg.attr("transform", "newValues"), which I do. Obvously it does not work like expected.

The view does change but seems to reset.

 var width = window.innerWidth,
            height = window.innerHeight;

        var svg = d3.select("body").append("svg")
            .attr("width", width)
            .attr("height", height)
            .call(d3.zoom().on("zoom", function (event) {
                svg.attr("transform", event.transform)
                console.log(event.transform)
            }))
            .append("g")

        ////////////////////////
        // outer force layout

        var data = {
            "nodes": [{
                "id": "A",
            },
            {
                "id": "B",
            },
            {
                "id": "C",
            },
            {
                "id": "D",
            },
            {
                "id": "E",
            },
            {
                "id": "F",
            },
            {
                "id": "G",
            },],
            "links": [{
                "source": "A",
                "target": "B"
            },
            {
                "source": "B",
                "target": "C"
            },
            {
                "source": "C",
                "target": "D"
            },
            {
                "source": "D",
                "target": "E"
            },
            {
                "source": "E",
                "target": "F"
            },
            {
                "source": "F",
                "target": "G"
            },]
        }

        var simulation = d3.forceSimulation()
            .force("size", d3.forceCenter(width / 2, height / 2))
            .force("charge", d3.forceManyBody().strength(-5000))
            .force("link", d3.forceLink().id(function (d) {
                return d.id
            }).distance(250))

        linksContainer = svg.append("g").attr("class", "linkscontainer")
        nodesContainer = svg.append("g").attr("class", "nodesContainer")


        links = linksContainer.selectAll(".linkPath")
            .data(data.links)
            .enter()
            .append("path")
            .attr("class", "linkPath")
            .attr("stroke", "red")
            .attr("fill", "transparent")
            .attr("stroke-width", 3)


        nodes = nodesContainer.selectAll(".nodes")
            .data(data.nodes, function (d) {
                return d.id;
            })
            .enter()
            .append("g")
            .attr("class", "nodes")
            .call(d3.drag()
                .on("start", dragStarted)
                .on("drag", dragged)
                .on("end", dragEnded)
            )
            .on("click", function(d) {

                //d3.zoomTransform(d3.select(this))
                
                let nodeX = d.srcElement.__data__.x
                let nodeY = d.srcElement.__data__.y

                zoomToNode(nodeX, nodeY)                
            })

        nodes.selectAll("circle")
            .data(d => [d])
            .enter()
            .append("circle")
            .attr("class", "circle")
            .style("stroke", "blue")
            .attr("r", 40)

        simulation
            .nodes(data.nodes)
            .on("tick", tick)

        simulation
            .force("link")
            .links(data.links)

        function tick() {
            links.attr("d", function (d) {
                var dx = (d.target.x - d.source.x),
                    dy = (d.target.y - d.source.y),
                    dr = Math.sqrt(dx * dx + dy * dy)

                return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
            })

            nodes
                .attr("transform", d => `translate(${d.x}, ${d.y})`);
        }

        function dragStarted(event, d) {
            if (!event.active) simulation.alphaTarget(0.3).restart();
            d.fx = d.x;
            d.fy = d.y;
        }

        function dragged(event, d) {
            d.fx = event.x;
            d.fy = event.y;
        }

        function dragEnded(event, d) {
            if (!event.active) simulation.alphaTarget(0);
            d.fx = null;
            d.fy = null;
        }

        function zoomToNode(thisX, thisY) {
            let transformValue = {"k": 2, "x": thisX, "y": thisY}

            console.log(transformValue)

            svg.attr("transform", transformValue)

            svg.attr("transform", event.transform)
        }
    .link {
        stroke: #000;
        stroke-width: 1.5px;
    }

    .nodes {
        fill: whitesmoke;
        cursor: pointer;
    }

    .buttons {
        margin: 0 1em 0 0;
    }
<!DOCTYPE html>
<html lang="de">

<head>
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
    <meta charset="utf-8">

    <!-- jQuery -->
    <script src="https://code.jquery.com/jquery-3.6.3.js"></script>
    <!-- D3 -->
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <!-- fontawesome stylesheet https://fontawesome.com/ -->
    <script src="https://kit.fontawesome.com/98a5e27706.js" crossorigin="anonymous"></script>
</head>


<body>

</body>

</html>


from D3 force graph - zoom to node

Saving a .CSV file to Google sheets from colab

I am reading from a Google sheets file in Colab, but I'd like to dump a pandas dataframe in its entirety to the colab. I've seen code snippets that deal with ranges, but I'd like some better way to "import" the entire CSV. Does anyone have the code for that?

from google.colab import auth
auth.authenticate_user()

import gspread
from google.auth import default
creds, _ = default()
gc = gspread.authorize(creds)
sh = gc.create('A new spreadsheet')


worksheet = gc.open('A new spreadsheet').sheet1

When reading I have the following code:

rows = worksheet.get_all_records()

What is the equivalent for dumping a dataframe i.e. via .csv

I have tried writing rows as:

worksheet = gc.open('Capital Gain Output').sheet1
worksheet.add_rows(results)

where results is a list of dictionaries (one for each row).



from Saving a .CSV file to Google sheets from colab

Tuesday 28 March 2023

How to apply rate limit based on method parameter

I'm using python module ratelimit to throttle a function, which calls a rest api, I need to apply throttle based on the method of the requests, e.g. for PUT/POST/DELETE 1 per 10s, for GET 5 per 1s, how can I achieve this without break the function into two?

from ratelimit import limits, sleep_and_retry

@sleep_and_retry
@limits(calls=1 if method != 'GET' else 5), period=10 if method != 'GET' else 1)
def callrest(method, url, data):
    ...

Is it possible to do this?



from How to apply rate limit based on method parameter

Android - How to determine which music app is currently playing?

I want to know what song is currently played on my Android phone. In the past, the solution would be to add an IntentFilter but now the solution should be based on MediaSession. Each media app posts its MediaSession. I have a the following listener:

private val mSessionsChangedListener =
    MediaSessionManager.OnActiveSessionsChangedListener { list: List<MediaController>? ->
        //...here
    }

This listener fires every time a change in the active sessions occur. Then I can utilise MediaMetadata in order to grab the info about the currently playing song. In case there is only 1 active MediaSession, that works. However in case there are multiple apps with active sessions, I'm not sure how to determine which one of the List<MediaController> is actually playing.

I tried checking the playbackState like this:

    list.forEach {
        if (it.playbackState?.state == PlaybackState.STATE_PLAYING) {
            currentMediaController = it
        }
    } 

But when switching between 2 media apps, the playbackState is not updated yet when this listener is triggered.

What is the correct way of determining which MediaController is actually playing?

Thanks!



from Android - How to determine which music app is currently playing?

Select2 not populating hidden field with values

I have select2 working in my WordPress plugin, and I am able to search for pages when I type into a field and click on them. You know how that works.

The problem is that the ids of the pages I select are not being added into the "exclusion_ids" hidden field. Here's my code:

<input name="exclusions" type="text" class="select2" />
<input name="exclusion_ids" type="hidden" />

<script>
    jQuery(document).ready(function($) {
        $('.select2').select2({
            ajax: {
                url: ajaxurl,
                dataType: 'json',
                delay: 250,
                data: function (params) {
                    return {
                        q: params.term,
                        action: 'search_posts',
                        nonce: '<?php echo wp_create_nonce( "search_posts_nonce" ); ?>'
                    };
                },
                results: function (data) {
                    var results = [];
                    $.each(data, function(index, post) {
                        results.push({
                            id: post.id,
                            text: post.title
                        });
                    });
                    return {
                        results: results
                    };
                },
                cache: true
            },
            minimumInputLength: 3,
            placeholder: 'Type to search for posts',
            tags: true,
            tokenSeparators: [',']
        });

        jQuery(document).on('change', '.select2', function() {
            var ids = [];
            $('.select2').find(':selected').each(function() {
                ids.push($(this).val());
            });
            $('input[name="exclusion_ids"]').val(ids.join(','));
        });
    });

    // Debugging
    jQuery(document).on('change', '.select2', function() {
        console.log('Change event fired!');
        var ids = [];
        jQuery('.select2').find(':selected').each(function() {
            ids.push(jQuery(this).val());
        });
        console.log('The ids :', ids);
        console.log('Hidden field value: ', jQuery('input[name="exclusion_ids"]').val());
    });
</script>

There are no errors in the console.

My debugging returns this:

Change event fired!

The ids : []
    length: 0
    [[Prototype]]: Array(0)

Hidden field value: 

When I inspect the hidden field while selecting a page from the field, I can see it suddenly change from this...

<input name="exclusion_ids" type="hidden">

...to this...

<input name="exclusion_ids" type="hidden" value="">

...so I do know the change event is firing and targeting the correct hidden field. So the problem looks like it's not getting the IDs.

Any ideas on how to fix this?



from Select2 not populating hidden field with values

Stream large XML file directly from GridFS to xmltodict parsing

I am using Motor for async MongoDB operations. I have a gridfs storage where I store large XML files (typically 30+ MB in size) in chunks of 8 MBs. I want to incrementally parse the XML file using xmltodict. Here is how my code looks.

async def read_file(file_id):
    gfs_out: AsyncIOMotorGridOut = await gfs_bucket.open_download_stream(file_id)

    tmpfile = tempfile.SpooledTemporaryFile(mode="w+b")
    while data := await gfs_out.readchunk():
        tmpfile.write(data)

    xmltodict.parse(tmpfile)

I am pulling all the chunks out one by one and storing them in a temporary file in memory and then parsing the entire file through xmltodict. Ideally I would want toparse it incrementally as I don't need the entire xml object from the get go.

The documentation for xmltodict suggests that we can add custom handlers to parse a stream, like this example:

>>> def handle_artist(_, artist):
...     print(artist['name'])
...     return True
>>> 
>>> xmltodict.parse(GzipFile('discogs_artists.xml.gz'),
...     item_depth=2, item_callback=handle_artist)
A Perfect Circle
Fantômas
King Crimson
Chris Potter
...

But the problem with this is that it expects a file-like object with a synchronous read() method, not a coroutine. Is there any way it can be achieved? Any help would be greatly appreciated.



from Stream large XML file directly from GridFS to xmltodict parsing

Add img/video specified in data-src to another container on hover

It's worth noting my actual UI is more complex but I've stripped everything back just to focus on the script needed in the example.

Issue One

I have a several div's that display content within them but also have a img or video path specified on a data-src. When hovering over an item, the relevant media (either img or video) should be added/displayed in a separate div.

In hindsight. I'm now wondering if the img/video tag shouldn't be on the page and is only added, along with the correct URL when requested?

I have the image working but not a way to check if it's an img/video and add to the relevant img or video tag. Ideally those tags wouldn't be on the page until needed but that's a bit beyond me at this stage. This should be ok as a proof of concept.

Issue Two (not a priority but I'll just mention)

I'm more than aware loading a large image or video could take time to load. So is there away to fade-in the image/video when it has loaded? I was thinking I could display a loading gif just before to give the user an indication something is happening before fading in?

const getAsset = document.querySelector('.carousel-bg img')

document.querySelectorAll('.swiper-slide').forEach(item => {
  item.onmouseover = () => getAsset.src = item.dataset.src
  item.onmouseout = () => getAsset.src = ""
})
/* Where Images Appear */

.carousel-bg {
  background: red;
  height: 300px;
  position: relative;
  margin-bottom: 10px;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  max-width: 600px;
}

.carousel-bg img,
.carousel-bg video {
  display: block;
  height: 100%;
  object-fit: cover;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100%;
}

/* Slides */

.swiper-wrapper {
  display: flex;
}

.swiper-slide {
  background: #eee;
  margin-right: 10px;
  padding: 20px;
}

.swiper-slide img {
  
}
<div class="carousel">

  <div class="carousel-bg">
    <img src="" />
    <video autoplay muted loop>
      <source src="" type="video/mp4">
    </video>
  </div>

  <div class="swiper mySwiper">
    <div class="swiper-wrapper">
      <div class="swiper-slide" data-src="https://images.pexels.com/photos/2387418/pexels-photo-2387418.jpeg">
        Slide 1 (img)
      </div>
      <div class="swiper-slide" data-src="https://www.w3schools.com/html/mov_bbb.mp4">
        Slide 2 (vid)
      </div>
      <div class="swiper-slide" data-src="https://images.pexels.com/photos/356807/pexels-photo-356807.jpeg">
        Slide 3 (img)
      </div>
    </div>
  </div>
</div>


from Add img/video specified in data-src to another container on hover

Where in build.gradle can I define the app name?

It may be a very simple question but I cannot find how to specify an app name in build.gradle in the format 'com.company.app'. Below is the documentation I am trying to follow.

enter image description here



from Where in build.gradle can I define the app name?

Monday 27 March 2023

How to save multiple figures from bokeh html file as separate png figures but download just as one zip file?

This question is based on another question, where the accepted answer already provide working code to save the multiple figures in a bokeh gridplot into separate png files.

However, with that answer, a number of png-figures are downloaded, each png-figure is a separate file. It is more convenient for my users to download all the png-figures just as one zip-file. Could you please show me how to do it?

Thanks.



from How to save multiple figures from bokeh html file as separate png figures but download just as one zip file?

how to make a data-attr read HTML

I have a data-attr Tooltip which display only text. Now I want to add <a href="google.com">Lorem</a> in one phrase. Like for example tooltip="Hello, this is <a href="google.com">Lorem</a> welcome"

But I can not implement the HTML because it will read it as Text. How can I make Tooltips/data-attr read HTML.

I thought of something like: read exact specific Text in Tooltip and replace it with <a href="google.com">Lorem</a> - but - that will still display it as Text.

/* START TOOLTIP STYLES */
[tooltip] {
  position: relative; /* opinion 1 */
}

/* Applies to all tooltips */
[tooltip]::before,
[tooltip]::after {
  text-transform: none; /* opinion 2 */
  font-size: .9em; /* opinion 3 */
  line-height: 1;
  user-select: none;
  pointer-events: none;
  position: absolute;
  display: none;
  opacity: 0;
}
[tooltip]::before {
  content: '';
  border: 5px solid transparent; /* opinion 4 */
  z-index: 1001; /* absurdity 1 */
}
[tooltip]::after {
  content: attr(tooltip); /* magic! */
  
  /* most of the rest of this is opinion */
  font-family: Helvetica, sans-serif;
  text-align: center;
  
  /* 
    Let the content set the size of the tooltips 
    but this will also keep them from being obnoxious
    */
  min-width: 3em;
  max-width: 21em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 1ch 1.5ch;
  border-radius: .3ch;
  box-shadow: 0 1em 2em -.5em rgba(0, 0, 0, 0.35);
  background: #333;
  color: #fff;
  z-index: 1000; /* absurdity 2 */
}

/* Make the tooltips respond to hover */
[tooltip]:hover::before,
[tooltip]:hover::after {
  display: block;
}

/* don't show empty tooltips */
[tooltip='']::before,
[tooltip='']::after {
  display: none !important;
}

/* FLOW: UP */
[tooltip]:not([flow])::before,
[tooltip][flow^="up"]::before {
  bottom: 100%;
  border-bottom-width: 0;
  border-top-color: #333;
}
[tooltip]:not([flow])::after,
[tooltip][flow^="up"]::after {
  bottom: calc(100% + 5px);
}
[tooltip]:not([flow])::before,
[tooltip]:not([flow])::after,
[tooltip][flow^="up"]::before,
[tooltip][flow^="up"]::after {
  left: 50%;
  transform: translate(-50%, -.5em);
}

/* FLOW: DOWN */
[tooltip][flow^="down"]::before {
  top: 100%;
  border-top-width: 0;
  border-bottom-color: #333;
}
[tooltip][flow^="down"]::after {
  top: calc(100% + 5px);
}
[tooltip][flow^="down"]::before,
[tooltip][flow^="down"]::after {
  left: 50%;
  transform: translate(-50%, .5em);
}

/* FLOW: LEFT */
[tooltip][flow^="left"]::before {
  top: 50%;
  border-right-width: 0;
  border-left-color: #333;
  left: calc(0em - 5px);
  transform: translate(-.5em, -50%);
}
[tooltip][flow^="left"]::after {
  top: 50%;
  right: calc(100% + 5px);
  transform: translate(-.5em, -50%);
}

/* FLOW: RIGHT */
[tooltip][flow^="right"]::before {
  top: 50%;
  border-left-width: 0;
  border-right-color: #333;
  right: calc(0em - 5px);
  transform: translate(.5em, -50%);
}
[tooltip][flow^="right"]::after {
  top: 50%;
  left: calc(100% + 5px);
  transform: translate(.5em, -50%);
}

/* KEYFRAMES */
@keyframes tooltips-vert {
  to {
    opacity: .9;
    transform: translate(-50%, 0);
  }
}

@keyframes tooltips-horz {
  to {
    opacity: .9;
    transform: translate(0, -50%);
  }
}

/* FX All The Things */ 
[tooltip]:not([flow]):hover::before,
[tooltip]:not([flow]):hover::after,
[tooltip][flow^="up"]:hover::before,
[tooltip][flow^="up"]:hover::after,
[tooltip][flow^="down"]:hover::before,
[tooltip][flow^="down"]:hover::after {
  animation: tooltips-vert 300ms ease-out forwards;
}

[tooltip][flow^="left"]:hover::before,
[tooltip][flow^="left"]:hover::after,
[tooltip][flow^="right"]:hover::before,
[tooltip][flow^="right"]:hover::after {
  animation: tooltips-horz 300ms ease-out forwards;
}










/* UNRELATED to tooltips */
body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  font-family: sans-serif;
  background: #ededed;
}
main {
  flex: 1 1 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
aside {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #49b293;
  color: #fff;
  padding: 1em;
}
main div {
  text-align: center;
  color: #353539;
}
main span {
  padding: .5em 1em;
  margin: .5em;
  display: inline-block;
  background: #dedede;
}

aside a {
  color: inherit;
  text-decoration: none;
  font-weight: bold;
  display: inline-block;
  padding: .4em 1em;
}
<main>
  <div>
    <span tooltip="I'm up above it!">Up</span>
  </div>
  <div>
    <span tooltip="Slide to the left" flow="left">Left</span>
    <span tooltip="Slide to the right" flow="right">Right</span>
  </div>
  <div>
    <span tooltip="Get Down." flow="down">Down</span>
  </div>
</main>


from how to make a data-attr read HTML

How to prevent transformer generate function to produce certain words?

I have the following code:

from transformers import T5Tokenizer, T5ForConditionalGeneration

tokenizer = T5Tokenizer.from_pretrained("t5-small")
model = T5ForConditionalGeneration.from_pretrained("t5-small")

input_ids = tokenizer("The <extra_id_0> walks in <extra_id_1> park", return_tensors="pt").input_ids

sequence_ids = model.generate(input_ids)
sequences = tokenizer.batch_decode(sequence_ids)
sequences

Currently it produces this:

['<pad><extra_id_0> park offers<extra_id_1> the<extra_id_2> park.</s>']

Is there a way to prevent the generator to produce certain words (e.g. park, offer) not in the list?



from How to prevent transformer generate function to produce certain words?

setEnableVolumeLogger not found

I am using the latest version of react-native-webrtc (106.0.7). When I try to build the android project, I’m getting these errors:

> /react-native-webrtc/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java:129:
> error: cannot find symbol adm =
> JavaAudioDeviceModule.builder(reactContext).setEnableVolumeLogger(false).createAudioDeviceModule();
> 

> /react-native-webrtc/android/src/main/java/com/oney/WebRTCModule/SerializeUtils.java:59:
> error: an enum switch case label must be the unqualified name of an
> enumeration constant case STOPPED:

The required method setEnableVolumeLogger and the enumeration STOPPED are not available in the JavaAudioDeviceModule.

How can I fix this issue?



from setEnableVolumeLogger not found

How to properly generate a documentation with Swagger for Flask

I would like to ask how to generate a proper documentation with Swagger for Flask. I have tried many libraries like flassger, apispec, marshmallow, flask_apispec, etc but I didn't find the best one.

So I have a function here:

@app.route('/test', methods=['POST])
def test_function():
    data = request.get_json()
    validated_data = TestSchema().load(data)
    # Doing something here
    return jsonify({'data': 'success'}), 200

I already have a schema object with marshmallow as follow:

class TestSchema(Schema):
    id = fields.Int(required=True)
    name = fields.Str(required=True)

I want to automatically generate the documentation so that it is easier to know how to call my test API. How can I do this with swagger UI? I'm not really familiar with web programming and APISpec things, so it is inconvenience for me to generate it manually my writing yaml file.



from How to properly generate a documentation with Swagger for Flask

How to make MLKIT not detect people in the background movement?

I could not find any function to count different people faces in 1 video real time. I want to detect head rotation but if there are 2 people, the getHeadEulerAngleY will detect the person in the background whose head is moving.

This is my detector option code:

        FaceDetectorOptions detectorOptions = new FaceDetectorOptions
            .Builder()
            .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
            .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL)
            .build();

Also, Is it possible to count how many people in live detection?



from How to make MLKIT not detect people in the background movement?

Sunday 26 March 2023

Is it possible to prevent the children of a component from being imported in React? (Next.js 13)

I have a <Navbar> component that is a client component.
Here is the implementation:

Navbar.tsx

"use client";
import { usePathname } from "next/navigation";
import { useEffect, type FC } from "react";

import type { WithChildren } from "@types";

const Navbar: FC<WithChildren> = ({ children }) => {
  const pathname = usePathname();

  useEffect(() => {
    const onPageLoad = async () => {
      const { toast } = await import("react-hot-toast");

      toast.remove();
    };

    onPageLoad();
  }, [pathname]);

  if (pathname === "/auth") return null;

  return <nav className="flex h-16 items-center justify-between bg-light-gray p-2 dark:bg-black dark:text-white">{children}</nav>;
};

export default Navbar;

I am using it in the <RootLayout /> of my app:

app/layout.tsx

<main className={`${inter.className} dark:bg-dark-gray dark:text-white`}>
  <Toaster toastOptions= />
  <Navbar> {/* <---- Client Component */}
    <Logo /> {/* <---- Server Component */}
    <SearchBar /> {/* <---- Client Component */}
    <NavLinks /> {/* <---- Server Component */}
  </Navbar> {/* <---- Client Component */}
  <Provider>{children}</Provider>
</main>

In the Navbar component, I am returning null if on the auth page.
The problem is that the children are still present.

Here is what I mean:

In the <NavLinks /> component:

// ...
const UserIcon = lazy(() => import("@components/UserIcon"), { ssr: false });
// ...

const NavLinks: FC = () => (
  <section className="ml-auto flex">
    {/* ... */}
    <UserIcon /> {/* Client Component */}
  </section>
);

export default NavLinks;

The <UserIcon /> component is only rendered on the client. It gets a token from document.cookie, then it decodes the user object from the token using the jwt-token package, And then displays it in the UI (Just a simple Image Component)

Here is the component:

UserIcon.tsx

"use client";
import lazy from "next/dynamic";

import type { FC } from "react";

import useSession from "@hooks/useSession";

const Image = lazy(() => import("next/image"));
const Link = lazy(() => import("next/link"));

const UserIcon: FC = () => {
  const user = useSession();

  return (
    <Link href={`/people/${user.name}`}>
      <Image src={user.picture} alt={user.name} height={40} width={40} className="rounded-full" priority />
    </Link>
  );
};

export default UserIcon;

useSession.ts

import jwtDecode from "jwt-decode";

import type { FirebaseUser, SessionHook } from "@types";

import { getCookie } from "@utils/cookies";

/**
 * ! Only use in components that are not rendered on the server or in a `useEffect`
 */
const useSession: SessionHook = () => {
  const authToken = getCookie("auth_token");
  const user = jwtDecode<FirebaseUser>(authToken);

  return user;
};

export default useSession;

The problem is that, because the <Navbar /> is declared in the RootLayout and the JWT validation is done in one of it's children (<NavLinks />), it tries to get and decode the token even on the Authentication page. (And since the user is on the auth page, the user is not logged in and the token doesn't exist so jwt-decode throws an error.

Is it possible to prevent the children of the <Navbar> component to not perform any logic or just prevent them from being created?

I have tried making the root layout a client component and useState there, but that causes some hydration problems that I can't seem to fix.

Also making the root layout a client component increases the bundle size by 10kb+ and the navbar that is not shown on the Auth page also ships dead code since the user is not going to see it at all until they login.

NOTE: The useSession hook used to be a server-side function which got the cookies from next/headers.
But this had a downside, because it was used in the and the navbar is present on every page, AND using cookies forces dynamic rendering (SSR) which is not ideal since many pages are better suited to be statically generated at build time or regenerate at runtime (ISR) That is why I switched to a client-side implementation.



from Is it possible to prevent the children of a component from being imported in React? (Next.js 13)

MacOS capture of still image from AVFoundation camera silently fails

Problem: I am trying to use a python function, capture_image(unique_id, dest_filename), for MacOS that is supposed to capture a still image from a video camera using the AVFoundation framework and the pyobjc-framework-AVFoundation framework.

Expected result: Given an AVFoundation unique_id and a dest_filename passed as parameters, a JPG still image should be captured from the camera with AVFoundation unique id. The image should be written to the JPG file with the name dest_filename.

In my test I called this function 5 times and I expected to see this output:

writing /tmp/images/image_1.jpg
writing /tmp/images/image_2.jpg
writing /tmp/images/image_3.jpg
writing /tmp/images/image_4.jpg
writing /tmp/images/image_5.jpg
list_of_image_files_written=['image_1.jpg','image_2.jpg','image_3.jpg','image_4.jpg','image_5.jpg']

Process finished with exit code 0

Observed result: The function silently fails to write the captured image to the desired file.

No Runtime errors are thrown.

In my test I called this function 5 times and I actually saw output (note that image directory is actually empty - no JPGs were actually written:

writing /tmp/images/image_1.jpg
writing /tmp/images/image_2.jpg
writing /tmp/images/image_3.jpg
writing /tmp/images/image_4.jpg
writing /tmp/images/image_5.jpg
list_of_image_files_written=[]

Process finished with exit code 0

The Pycharm IDE does show these compile time errors and warnings:

  • Unresolved reference 'NSData': 31
  • Unresolved reference 'NSDataWritingAtomic': 32
  • Cannot find reference 'AVCaptureSession' in '__init__py | __init__py: 8
  • Cannot find reference 'AVCaptureDevice' in '__init__py | __init__py: 9
  • Cannot find reference 'AVMediaTypeVideo' in '__init__py | __init__py: 9
  • Cannot find reference 'AVCaptureDeviceInput' in '__init__py | __init__py: 17
  • Cannot find reference 'AVCaptureStillImageOutput' in '__init__py | __init__py: 18
  • Cannot find reference 'AVMediaTypeVideo' in '__init__py | __init__py: 27
  • Cannot find reference 'AVVideoCodecKey' in '__init__py | __init__py: 28
  • Cannot find reference 'AVVideoCodecTypeJPEG' in '__init__py | __init__py: 28
  • Cannot find reference 'AVCaptureStillImageOutput' in '__init__py | __init__py: 31
  • Local variable 'output_settings' value is not used: 28
  • PEP 8: E128 continuation line under_indented for visual indent: 30
  • PEP 8: E128 continuation line under_indented for visual indent: 31

I am running on MacOS Ventura 13.2.1 using PyCharm.

I have searched the AVFoundation webpages as well as OpenStack and google, but without success in finding a better example.

The code:

import os

import AVFoundation
import time


def capture_image(unique_id, dest_filename):
    # Set up AVFoundation capture session
    session = AVFoundation.AVCaptureSession.alloc().init()
    devices = AVFoundation.AVCaptureDevice.devicesWithMediaType_(AVFoundation.AVMediaTypeVideo)
    device = None
    for dev in devices:
        if dev.uniqueID() == unique_id:
            device = dev
            break
    if device is None:
        raise ValueError("No camera found with unique ID: " + unique_id)
    input_session = AVFoundation.AVCaptureDeviceInput.deviceInputWithDevice_error_(device, None)[0]
    output_session = AVFoundation.AVCaptureStillImageOutput.alloc().init()
    session.addInput_(input_session)
    session.addOutput_(output_session)
    session.startRunning()

    # Wait for the capture to be ready
    time.sleep(2)

    # Capture the image
    connection = output_session.connectionWithMediaType_(AVFoundation.AVMediaTypeVideo)
    output_settings = {AVFoundation.AVVideoCodecKey: AVFoundation.AVVideoCodecTypeJPEG}
    output_session.captureStillImageAsynchronouslyFromConnection_completionHandler_(connection,
        lambda buffer, error:
            NSData.dataWithData_(AVFoundation.AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation_(buffer))
            .writeToFile_options_error_(dest_filename, NSDataWritingAtomic, None))

    # Stop the session
    session.stopRunning()
    print(f'writing {dest_filename}')
    return dest_filename


if __name__ == '__main__':
    images_dict = {
        '0x143141300bda5829': '/tmp/images/image_1.jpg',
        '0x143141100bda5829': '/tmp/images/image_2.jpg',
        '0x143200000bda5829': '/tmp/images/image_3.jpg',
        '0x143300000bda5829': '/tmp/images/image_4.jpg',
        '0x143121200bda5829': '/tmp/images/image_5.jpg',
    }
    for the_unique_id in images_dict:
        capture_image(the_unique_id, images_dict[the_unique_id])
    list_of_image_files_written = os.listdir('/tmp/images')
    print(f'{list_of_image_files_written=}')


from MacOS capture of still image from AVFoundation camera silently fails

How to check if the drone is armed or not using MAVLink/MAVProxy?

I want to run a python script when the drone is armed and stop running that python script when the drone is disarmed using MAVProxy. To check that I want to first check if the drone is armed or not. I tried 2 types of code but both of them are giving inconsistent results. It is showing armed but after some time it shows disarmed even though its armed.

I tried 2 types of code but both of them are giving inconsistent results. It is showing armed but after some time it shows disarmed even though its armed.

CODE 1:

from pymavlink import mavutil
#import sensor # logging file
import time
import sys

# Start a connection listening on a UDP port
# Once connected, use 'the_connection' to get and send messages


master = mavutil.mavlink_connection('/dev/ttyACM0')


while True:
       


# Wait for the connection to establish
 master.wait_heartbeat()


#Check if the drone is armed

 if master.motors_armed():
    print("The drone is armed.")
    #sensor.s_info('on')
    

 else:
    print("The drone is disarmed.")
    #sensor.s_info('off')

 time.sleep(2)

code 2:

 import time
 from pymavlink import mavutil
 import sensor # logging file

 # create a MAVLink connection
 master = mavutil.mavlink_connection('/dev/ttyACM0')

 while True:

 # check if the drone is armed
  armed = False



     # get the current system status
  msg = master.recv_match(type='HEARTBEAT', blocking=True)

     # check if the drone is armed

  armed = (msg.base_mode & mavutil.mavlink.MAV_MODE_FLAG_SAFETY_ARMED)
  if armed:
     print('Drone is armed')
     sensor.s_info('on')
 
  else:
     print('Drone is disarmed')
     sensor.s_info('off')


from How to check if the drone is armed or not using MAVLink/MAVProxy?

Object properties must be on a new line with ESLint

let's say I have the following variable:

const myObject = { property1: 'value1', property2: 'value2', property3: 'value3' };

I want to enforce an eslint rule so that if an object has a minimum of three properties, then each property must go on a new line, such as below:

const myObject = {
  property1: 'value1',
  property2: 'value2',
  property3: 'value3',
};

However, if I use the eslint rule object-curly-newline, it appears that the following is acceptable, and this is what get's automatically formatted when eslint --fix runs:

const myObject = {
  property1: 'value1', property2: 'value2', property3: 'value3',
};

How can I enforce an eslint rule that makes sure that each new property is on a new line? Thanks!



from Object properties must be on a new line with ESLint

Trying to use the DOMParser with node js

I am running into issues when trying to use the DOMParser in my js code. In my code, I retrieve an xml file via xmlhttp.responseText soap response. I want to be able to access its elements in JSON format, so my code looks like:

var xml = new DOMParser();
xml = xml.parseFromString(xmlhttp.responseText, 'text/xml');
var result = xmlToJson(xml);

I get this error message: ReferenceError: DOMParser is not defined

Edit: This link hasn't worked for me because my javascript isn't in the HTML page, as it is a node.js file. JavaScript DOMParser access innerHTML and other properties



from Trying to use the DOMParser with node js